A few months ago I was integrating fiscal identifier validation into a project. I googled it, found a 30-line JavaScript function, copied it, tested it with four cases, and it worked. I dropped it into the codebase and forgot about it.
Three months later, a user emailed us: their CIF wasn't validating. It was a perfectly valid CIF.
That's when I understood there's a difference between implementing validation and maintaining it correctly.
The problem isn't that it's hard
Validating a NIF seems straightforward: 8 digits, one letter, modulo 23. Any developer can implement it in ten minutes.
The problems appear when you scratch the surface:
NIF: the basic algorithm works, but there are special formats — K, L and M NIFs for specific cases — that most implementations simply ignore.
NIE: starts with X, Y or Z, then follows the NIF algorithm with substitution. Most regex you'll find online accept 7 or 8 digits interchangeably, when the correct format depends on the prefix.
CIF: this is where almost everything breaks. The control character can be a letter or a digit depending on the entity type (first letter of the CIF). Most implementations accept either for all types — which is incorrect. Additionally, when (10 - sum % 10) % 10 === 0, the control character should be J in certain cases — an edge case that produces silent bugs that are extremely hard to track down.
IBAN: the MOD-97 algorithm requires arithmetic with very large numbers. In JavaScript you need BigInt, and in environments where it's unavailable or poorly implemented, it fails without a visible error.
None of these bugs surface in development. They surface in production, with real identifiers from real users.
The silent bug is the worst kind of bug
When a validation fails visibly — throws an exception, returns an error — you catch it and fix it. The real problem with fiscal validation bugs is that they accept invalid identifiers without warning.
A user enters a CIF with an incorrect control digit. Your code says "valid". The data goes into the database. Months later, when you try to use that data for a fiscal operation, the problem surfaces — but now you have no idea where it came from.
The maintenance nobody budgets for
Even if you implement everything correctly today, there's another problem: the rules can change.
Spain has updated fiscal identifier formats in the past. NIE formats have evolved. When that happens, someone has to update the code — and that someone might not be the person who wrote it, or be on the same team, or remember why each decision was made.
With an API, that's the API maintainer's problem. You consume the endpoint, they keep the algorithms up to date.
What we built
We built Valix — a REST API that validates NIF, NIE, CIF and IBAN using the official Spanish algorithms, with automatic type detection and a structured JSON response.
import requests
response = requests.post(
"https://api.getvalix.io/v1/validate/trial",
json={
"items": [
{"value": "12345678Z", "type": "AUTO"},
{"value": "X1234567L", "type": "AUTO"},
{"value": "A12345674", "type": "AUTO"},
{"value": "ES9121000418450200051332", "type": "AUTO"}
]
}
)
result = response.json()
for item in result["results"]:
status = "valid" if item["valid"] else f"invalid: {', '.join(item['errors'])}"
print(f"{item['value']} → {item['detected_type']} — {status}")
Output:
12345678Z → NIF — valid
X1234567L → NIE — valid
A12345674 → CIF — valid
ES9121000418450200051332 → IBAN — valid
The trial endpoint requires no registration or API key — 50 validations per day per IP so you can test it before committing to anything.
When does it make sense to implement it yourself?
Being honest: if your validation volume is very low, you're always validating the same identifier type, and you have exhaustive tests with real-world cases, implementing it internally can make sense.
But if you're validating identifiers from production users, mixing types (NIF + NIE + CIF + IBAN), or simply don't want to spend time maintaining this — an API is the more sensible choice.
Resources
- API and documentation: getvalix.io
- Code examples in Python, JavaScript, PHP and cURL: github.com/getvalix/valix-examples
- JavaScript/TypeScript SDK: npm @valix/sdk
Disclaimer: I'm the author of Valix.
Top comments (0)