Try the structural validation free, right here. When you need to know whether the voter ID is current in the roll —or was reported stolen or lost— the API answers it in a single call, with INE's official status.
Technical questions? Talk to an engineer on WhatsApp — 0% bots.
Structural validation in your browser — we identify the credential model from its numbers, we don't send them to any server. The Lista Nominal status is answered by the API.
The number the API needs depends on the credential model. All three live in the machine-readable zone on the back; the elector key is on the front and this service doesn't use it.
Our own schematic of the back (example numbers). The first MRZ line carries the CIC and the OCR; the Citizen Identifier is the last 9 digits of the OCR. The elector key is on the front and this service doesn't use it.
MODEL DCIC + OCRA 9-digit CIC and a 13-digit OCR, both in the MRZ on the back. Issued through 2019; no longer valid for voting, but still presented as ID.MODELS E–JCIC + Citizen IdentifierA 9-digit CIC and a 9-digit Citizen Identifier —the last digits of the OCR— in the MRZ on the back. These are the current models (E since 2014, G/H since 2019, I/J since 2026).MODELS A–CElector key + OCRThe oldest ones use the elector key (18) and the OCR. Not current; validated with the legacy combination.In the JSON, these numbers travel under English names. Here's how they map to the credential:
cic — CIC · Credential Identification Code (9 digits). Always required.ocr — OCR (13 digits). Present on model D.citizenIdentifier — Citizen Identifier (9 digits, the last ones of the OCR). Present on models E–J.You can send all three: the API works out which one to use from what it receives, so you don't have to identify the credential model before calling.
Every validation confirms whether the credential exists in INE's roll and what state it's in. That's where fraud detection lives: a credential with perfect data may be reported as stolen.
CURRENT
Current in the roll
It exists in the Lista Nominal and is current. You can accept it as ID with confidence.
type: "VALID_VOTER" · isValid: true
REVIEW
Not current or suspended
It's in the roll but not current for voting, or suspended by court order. Don't accept it as valid ID without more signals.
type: "INE_NOT_VALID"type: "SUSPENDED_BY_JUDICIAL_ORDER"ALERT
Stolen, lost or not registered
Reported stolen or lost, or absent from the roll: a direct signal of a borrowed or fake credential. Stop before continuing.
type: "REPORTED_AS_STOLEN_OR_LOST"type: "INE_NOT_FOUND"And you don't interpret codes: the envelope names the case in the type field — VALID_VOTER, REPORTED_AS_STOLEN_OR_LOST, SUSPENDED_BY_JUDICIAL_ORDER, MISMATCHED_OCR and more. Beyond the status it returns the elector key, the electoral geography (federal and local district), the registration, emission and validity years, and INE's official message.
A fake voter ID is almost never caught by eye. It is caught because its numbers don't add up, because it doesn't exist in the roll, or because the person presenting it is not the person in the photo. Each check rules out a different kind of fraud.
| Check | What it catches | How it's answered |
|---|---|---|
| 1 · The numbers on the back | Made-up or miscopied numbers: the CIC has 9 digits, the OCR 13, and on models E–J the last 9 of the OCR are the Citizen Identifier. | Free, in the demo on this page |
| 2 · It exists in the Lista Nominal | An ID that isn't registered in the roll, or a CIC and an OCR that don't belong to the same ID. | INE_NOT_FOUND · MISMATCHED_OCR |
| 3 · The status of the ID | A genuine ID that was reported stolen or lost, is suspended by court order, or is no longer current. | REPORTED_AS_STOLEN_OR_LOST · SUSPENDED_BY_JUDICIAL_ORDER · INE_NOT_VALID |
| 4 · The person is the one in the photo | A genuine, current ID presented by someone who is not its holder. | Face match, another service in the catalog |
These checks don't inspect the plastic —holograms, inks or relief—: they confirm the official record and the face. That's why they are used together: each one covers what the previous one can't see. Checks 2 and 3 are a single call to the API.
The type field is your decision point. Three paths: proceed if the credential is valid, review in detail if the data is ambiguous or may have been mis-entered, reject if there's a fraud or invalidity signal. A switch over type and your onboarding decides on its own.
| type | What it means | Decision |
|---|---|---|
VALID_VOTER | Current in the Lista Nominal: works as ID and for voting. | Proceed |
VALID_FOREIGN_VOTER | Current for voting from abroad. Valid credential. | Proceed |
VALID_AS_ID_ONLY | Current only as photo ID (not for voting). Valid as ID. | Proceed |
MISMATCHED_OCR | The OCR doesn't match the CIC in the registry. Usually a data-entry error — or tampering. | Review |
INE_NOT_FOUND | The credential doesn't appear in the Lista Nominal. Could be a wrong entry or a nonexistent document. | Review |
SUSPENDED_BY_JUDICIAL_ORDER | Rights suspended by court order. Not valid for voting; review it per your use case. | Reject |
REPORTED_AS_STOLEN_OR_LOST | Reported stolen or lost, with a report number. A direct signal of a borrowed credential. | Reject |
INE_NOT_VALID | Not current as ID or for voting. Expired or deregistered credential. | Reject |
Beyond these business cases, technical errors arrive with status: "ERROR": a malformed CIC or OCR is INVALID_REQUEST with billable: false (no charge), and if INE doesn't respond at that moment, SERVICE_UNAVAILABLE (retry). You never pay for a case that wasn't resolved.
We query INE's Lista Nominal on every call, no cache. Same envelope as every OrigoID service: learn the contract once and it works for INE, CURP, RFC, IMSS and more.
INVALID_REQUEST and billable: false — you don't spend a credit on junk.const res = await fetch("https://api.origoid.com/mex/id/v1/voter-list-validations", { method: "POST", headers: { "x-api-key": process.env.ORIGOID_API_KEY, "content-type": "application/json", }, body: JSON.stringify({ cic: "123456789", ocr: "0123456789012" }), }); const result = await res.json();
import os, requests res = requests.post( "https://api.origoid.com/mex/id/v1/voter-list-validations", headers={ "x-api-key": os.environ["ORIGOID_API_KEY"], "content-type": "application/json", }, json={"cic": "123456789", "ocr": "0123456789012"}, timeout=30, ) result = res.json()
curl -X POST https://api.origoid.com/mex/id/v1/voter-list-validations \ -H "x-api-key: $ORIGOID_API_KEY" \ -H "content-type: application/json" \ -d '{"cic": "123456789", "ocr": "0123456789012"}'
{
"status": "OK",
"type": "VALID_VOTER",
"data": {
"isValid": true,
"personalInfo": { "electorKey": "GOMEZJ80010112H300" },
"validity": { "emissionYear": "2023", "validUntilYear": "2033" },
"validationStatus": { "stolenReport": null, "officialMessage": "Está vigente como medio de identificación y puede votar" }
},
"billable": true
}
A current credential doesn't prove the bearer is its holder. That's the other half of identity, and another service in the catalog answers it: face match between the selfie and the document photo.
Nobody types the CIC and the OCR by hand: INE OCR extracts them from the image, along with the name, the CURP and the address already split into fields. Its output is the input of this validation.
Several providers validate INE. What changes: whether they flag a stolen credential, what happens to your user's data afterward, and how much it costs to integrate.
Two levels: the structural validation (that the CIC is 9 digits and the OCR is 13, on the back) works offline — the demo above does it free in your browser. The real validation against INE's Lista Nominal — whether it's registered and current, or reported stolen or lost — is answered by the API in a single call.
Whether it's current in the Lista Nominal, the elector key, the electoral geography (federal and local district), the registration, emission and validity years, whether it was reported stolen or lost (with a report number) and INE's official message. The type classifies the case —VALID_VOTER, REPORTED_AS_STOLEN_OR_LOST, SUSPENDED_BY_JUDICIAL_ORDER, INE_NOT_FOUND, MISMATCHED_OCR and more— and the table above has the full list with the decision each one calls for.
It depends on the model. All use the CIC (9 digits, back). Model D adds the OCR (13 digits); models E, F, G, H, I and J add the Citizen Identifier (9 digits, the last ones of the OCR). And you don't have to work out the model before calling: you can send all three —CIC, OCR and Citizen Identifier— and the API works out which to use from what it receives.
The Lista Nominal confirms whether the credential exists and is current, and whether it was reported stolen or lost. One that doesn't appear in the roll (INE_NOT_FOUND) or whose OCR doesn't match the CIC (MISMATCHED_OCR) is a direct signal of a tampered or borrowed document. To confirm the photo matches the person, combine it with face match — another service in the catalog.
The CIC is the Credential Identification Code: 9 digits that identify that particular ID. It's on the back, on the first line of the machine-readable zone, right after the letters IDMEX. It's the only value the API always requires.
They are the first five letters of the machine-readable zone: ID marks it as an identity document and MEX is the issuing country. The 9 digits that follow are the CIC, then a check digit and, at the end of the line, the 13-digit OCR.
Yes — it's another service in the same catalog and it answers the other half of the question. You send it the selfie and the front of the document in Base64 and it returns a 0–100 score, the isMatch decision against a configurable threshold (90 by default, KYC-grade), the detected document type and a selfie quality analysis. It accepts any official document worldwide, not just the INE. See the full detail.
No. Every query is validated, answered and discarded, without storing the data. Zero retention across all our regions.
You start free: courtesy credits, no card. Then the price per validation drops with your volume. All plans published, no "contact us". Invalid format = billable: false, no charge.
Courtesy credits to validate real credentials in production. Access is enabled after a brief validation of your company.