Spanish CIF / NIF / NIE validator
Check Spanish tax IDs one by one or in bulk. For company CIFs, the first letter tells you the legal form, so the tool also sorts your list by company type.
Nothing yet. Paste numbers or a page on the left, or load the sample.
Runs entirely in your browser. A passing check means the number is well-formed, not that it is registered or active. Categories are inferred from the number format.
What gets checked
- NIF (8 digits + letter) and NIE (X/Y/Z + 7 digits + letter) are checked against the mod-23 letter table.
- CIF (letter + 7 digits + control) uses the AEAT control-digit algorithm. Some letters must end in a digit, some in a letter.
- The first CIF letter gives the legal form, and digits 2-3 are the province code where it was issued.
- An ES prefix (the VAT form) is accepted and stripped.
To confirm a number is registered and active, use AEAT. The Spanish Tax Agency and the Registro Mercantil (BORME) confirm whether a CIF belongs to an active company.
CIF letters and legal forms
| A | Sociedad Anónima (public limited company) |
| B | Sociedad Limitada (private limited company) |
| C / D | General / limited partnership |
| E | Comunidad de bienes |
| F | Cooperative |
| G | Association or foundation |
| H | Owners' association |
| J | Civil partnership |
| N / W | Foreign entity / permanent establishment |
| P / Q / S | Local authority / public body / central government |
| R | Religious institution |
| U / V | Joint venture (UTE) / other |
Run the same checks in your own code
import re
TYPES = {"A": "S.A.", "B": "S.L.", "F": "Cooperative", "G": "Association/foundation"}
def cif_ok(cif: str) -> bool:
c = re.sub(r"[\s.-]", "", cif.upper())
if not re.fullmatch(r"[ABCDEFGHJNPQRSUVW]\d{7}[0-9A-J]", c):
return False
total = 0
for i, d in enumerate(map(int, c[1:8])):
if i % 2: # even positions: add
total += d
else: # odd positions: double, add the digits
total += sum(divmod(d * 2, 10))
digit = (10 - total % 10) % 10
letter = "JABCDEFGHI"[digit]
if c[0] in "NPQRSW": return c[8] == letter
if c[0] in "ABEH": return c[8] == str(digit)
return c[8] in (letter, str(digit))
def nif_ok(nif: str) -> bool:
n = nif.upper().replace("X", "0", 1).replace("Y", "1", 1).replace("Z", "2", 1)
return bool(re.fullmatch(r"\d{8}[A-Z]", n)) and "TRWAGMYFPDXBNJZSQVHLCKE"[int(n[:8]) % 23] == n[8]
print(cif_ok("B-12345674"), TYPES.get("B"), nif_ok("12345678Z")) # True S.L. TrueCollect the numbers from company websites
A browser can't fetch other sites for you. Irmu fetches the page, handles rendering and blocking, and ai_query (+5 credits) returns the fields you ask for.
# Fetch a company's legal page with Irmu and let ai_query pull the IDs out,
# then validate them locally with the functions above.
import os, requests
resp = requests.get(
"https://app.irmu.com/api/crawl",
headers={"Authorization": f"Bearer {os.environ['IRMU_API_KEY']}"},
params={
"url": "https://example.com/legal",
"js": "true",
"ai_query": "Find the company's legal name, CIF/NIF and registered address on this page (usually in the Aviso legal). Return JSON: {name, cif, address}.",
},
timeout=120,
)
resp.raise_for_status()
print(resp.json()["data"])Build a verified company database
Fetch legal pages at scale with one API. 200 free credits every month, no card required.