Home / Use cases / Custom

Custom

Validate Spanish NIF and CIF numbers

Validate Spanish tax identifiers (NIF/CIF/NIE) through the VIES system. Understand the different formats for companies, individuals, and foreign nationals in Spain.

Spain uses three identifier formats under the umbrella of NIF (Número de Identificación Fiscal). For legal entities (S.L., S.A., cooperatives, etc.), the identifier is a letter + 7 digits + a control character (e.g. A12345678) — these are often called CIF historically, but the official term since 2008 is NIF for legal persons. For Spanish individuals, the NIF is the DNI: 8 digits + a letter (e.g. 12345678Z), computed from a fixed letter table on the number modulo 23. For foreign nationals, the NIE replaces the first digit with X, Y, or Z followed by 7 digits and a letter (e.g. X1234567L).

Only legal-entity NIFs are registered in VIES with the ES prefix; individual NIFs and NIEs are never VIES-registered because Spanish individuals are not VAT taxable persons. Before calling the TaxID API, your application should detect the identifier type: if it starts with a letter that corresponds to the legal-entity alphabet (A, B, C, D, E, F, G, H, J, N, P, Q, R, S, U, V, W), proceed with a VIES lookup; if it matches the DNI or NIE pattern, validate the checksum locally and capture it for invoicing without querying VIES.

The Agencia Tributaria (AEAT) processes ES VIES queries. Response times are generally under 1 second, but the AEAT node can be slow during peak periods (end of fiscal quarter). For the VIES query, pass the full identifier including the ES prefix to GET /api/v1/validate/ES/:nif. The company_address returned typically includes the registered address from the AEAT's records, which should be stored for legal invoicing compliance.

Implementation steps

  1. 1

    Identify NIF vs CIF vs NIE format

    Run the input through three sequential regex checks: /^[ABCDEFGHJNPQRSUVW][0-9]{7}[0-9A-J]$/ for legal-entity NIFs (CIF), /^[0-9]{8}[A-Z]$/ for individual DNI/NIF, and /^[XYZ][0-9]{7}[A-Z]$/ for NIE. Only legal-entity NIFs should trigger a VIES lookup; for DNI and NIE patterns, validate the checksum locally and store the identifier for invoice rendering without calling the external API.

  2. 2

    Validate checksum locally before API call

    For legal-entity NIFs, compute the control character using the AEAT's algorithm: sum odd-position and even-position digits separately (with a conversion table for letters in even positions), derive the modulo-10 remainder, and look up the expected control character. For individual NIFs/DNIs, use the modulo-23 algorithm against the fixed letter sequence 'TRWAGMYFPDXBNJZSQVHLCKE'. Reject numbers that fail the local checksum with a specific error message before consuming an API call.

  3. 3

    Query VIES for active registration

    Call GET /api/v1/validate/ES/:nif for legal-entity NIFs that pass local checksum validation. The ES country code instructs TaxID to query the AEAT's VIES node. Parse the response: status: 'active' confirms the entity is VIES-registered and eligible for reverse-charge; status: 'invalid' means the NIF is not in VIES (the entity may exist but not be VAT-registered for EU transactions); status: 'service_unavailable' means the AEAT node is temporarily unreachable.

  4. 4

    Return AEAT-confirmed company details

    On a valid response, store the company_name and company_address from the TaxID response in your customer record. Spanish invoicing regulations (Real Decreto 1619/2012) require B2B invoices to include the buyer's NIF and registered name; using the AEAT-confirmed company_name from VIES eliminates discrepancies that arise when buyers provide a trade name instead of their registered legal entity name.

Code example

Node.js

const res = await fetch(
  'http://localhost:3000/api/v1/validate/DE/ESA12345678',
  { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
);
const { valid, status, company_name, company_address } = await res.json();

if (valid) {
  console.log(`Valid EU business: ${company_name}`);
} else if (status === 'service_unavailable') {
  // VIES is temporarily down — retry or allow with manual check
  console.log('VIES unavailable — check back in a few minutes');
} else {
  console.log('Invalid VAT number — charge local tax rate');
}

Python

import requests

res = requests.get(
    "http://localhost:3000/api/v1/validate/DE/ESA12345678",
    headers={"Authorization": "Bearer YOUR_API_KEY"}
)
data = res.json()

if data["valid"]:
    print(f"Valid: {data['company_name']}")
elif data["status"] == "service_unavailable":
    print("VIES temporarily unavailable")
else:
    print("Invalid VAT number")

API response

The TaxID API returns a consistent JSON response for every validation request:

200 OK (active)valid
{
  "valid": true,
  "status": "active",
  "country_code": "DE",
  "vat_number": "123456789",
  "company_name": "Example GmbH",
  "company_address": "Musterstraße 1, 10115 Berlin",
  "request_date": "2026-05-10T00:00:00.000Z",
  "cached": false,
  "request_id": "req_01j..."
}

Error handling

The API uses a consistent Stripe-style error format. Always handle service_unavailable separately — VIES has occasional downtime and you should not reject valid customers during outages.

active

VAT number is valid and the business is registered

invalid

VAT number format is wrong or not registered in VIES

service_unavailable

VIES or the national system is temporarily down — retry later

Country-specific guides

Further reading

Evaluating EU VAT APIs? Compare TaxID with:

Ready to implement?

Get your free API key and start validating EU VAT numbers today.

Get free API key

Related use cases

EU VAT compliance for SaaS billing

Handle EU VAT for SaaS subscriptions. Validate customer VAT numbers at signup, determine B2B vs B2C ...

DAC7 marketplace seller VAT verification

Verify seller VAT numbers during marketplace onboarding for DAC7 compliance. EU platforms must colle...

Bulk VAT number validation via CSV import

Validate hundreds of EU VAT numbers in batch using parallel API requests. Ideal for CRM data cleansi...