INE · OCR and address

From the photo of the credential to data your backend can use.

You send the image and get back the name, CURP, elector key, CIC and OCR already separated. And the address —which on the INE comes abbreviated and on a single line— arrives broken into fields and validated against the official catalog.

address in fieldsMRZ and QR cross-checkedzero retention
domicilio-normalizadoREAL CASES FROM THE API

The expensive field

The address on an INE is made to be read, not processed.

It comes in three lines of free text, in caps, with abbreviations —C for calle, AV for avenida, FRACC for fraccionamiento, COL for colonia— and often with no separation between where the street ends and the number begins. Most OCRs hand it back to you exactly like that, and taking it apart is your job.

What almost every OCR does It gives you the three lines as strings. To store them in your file you have to write your own cleanup rules, maintain a catalog of neighborhoods and postal codes, and pay for a separate normalization service.
What OrigoID does Beyond the raw text, it returns the address already split into street, exterior number, interior number, neighborhood, postal code, municipality, state and country, with abbreviations expanded and the neighborhood, municipality and state validated against the official catalog. In the same call, at no extra cost.

And it doesn't hand it over blindly: the geocodingStatus field tells you how far the match got —VERIFIED at street or house level, PARTIAL when only the locality or postal code could be confirmed, UNVERIFIED when there was no confident match—. With that your code decides whether the address is good enough for the file or whether it's worth asking for a proof of address.

To be clear
What it returns

One image in. The whole document out, structured.

The front is required; the back is optional and adds the keys and the cross-checks. Both images go in Base64 (JPG or PNG).

personalInfoFirst and last names separately, date of birth, sex, CURP and elector key. No manual transcription and no splitting name strings by hand.
addressNormalizedThe address broken into fields and validated against the catalog, with its geocodingStatus. Alongside the original printed text in address, in case you need it as-is.
securityThe keys from the back: cic, ocr, citizenIdentifier, the full mrz and the qr when the model carries one. These are what you need to validate against the Lista Nominal.
validityRegistration, emission and validity years, plus the emission number. What's printed on the document — the real validity is confirmed by the Lista Nominal.
electoralGeographyElectoral state, municipality, section and locality. Useful to cross-check against the Lista Nominal response.
files[]The holder's photograph cropped from the document, in Base64. It's what you pass to face match to confirm the person presenting it is the one on the document.

The credential model also comes back —D, E, F, G, H or I— because each model carries different security features and different keys. Your code doesn't have to guess which one it's dealing with.

Two readings of the same document

What's printed against what's encoded.

A credential stores the same data in three places: printed on the front, encoded in the machine-readable zone on the back and, depending on the model, in the QR code. When you send the back, we read all three and contrast them.

FieldWhat it contrasts
mrzValidation.dateOfBirthThe printed date of birth against the one encoded in the MRZ.
mrzValidation.sexThe printed sex against the one in the MRZ.
mrzValidation.validityThe printed validity year against the one in the MRZ.
mrzValidation.emissionThe printed emission number against the one in the MRZ.
mrzValidation.cicThe printed CIC against the one in the MRZ.
mrzValidation.nameThe printed name against the one in the MRZ.
qrValidation.*CIC, OCR and citizen identifier from the QR against what's printed. Arrives as null if the model has no readable QR.

Each flag comes back true or false. A false doesn't by itself rule the credential fake —an OCR can get a blurry surname wrong— but it points at exactly which field doesn't line up so you can review it instead of rejecting the whole document blindly.

Before you send the photo

What gets rejected and what's only a recommendation.

There are two categories, and the difference matters for your invoice: hard limits are rejected before any processing and aren't charged; recommendations aren't enforced —an imperfect photo still gets processed—.

WhatLimitIf it isn't met
FormatJPEG · PNGINVALID_REQUEST, no charge
Size per imageup to 12 MB in Base64INVALID_REQUEST, no charge
Total request sizeup to 28 MBINVALID_REQUEST, no charge
Resolution, framing and lightingrecommendation, not enforcedprocessed anyway; if it can't be read, IMAGE_UNREADABLE and it is charged

Watch the size: the 12 MB limit applies to the string already encoded in Base64, and Base64 inflates a file by about a third. A 9 MB JPG on disk lands near 12 MB once encoded. In practice you won't get close: a well-taken phone photo weighs between 1 and 3 MB, and the lighter it is, the faster the round trip.

The ideal capture

None of this is enforced —a photo that doesn't comply still gets processed— but it's what raises the first-try read rate.

ParameterTargetWhy
Resolution1280 px wide or more
minimum 1024 px
At that width the text on the credential reads cleanly. Any modern phone exceeds it easily.
The credential in framemore than 90%What matters isn't the photo's pixels, it's the ones that land on the credential. A 4000 px photo where the credential takes up a fifth of the frame has less usable detail than a 1280 px one where it fills it.
Format and sizeJPG · 1–3 MBThe JPG straight out of the camera is enough. Re-saving it degrades it at every step; send it exactly as it came.
The whole credential in frameAll four edges visible, the corners inside the frame and the data clear, with no fingers over it.
Head-onCamera parallel to the credential. That way the letters keep their shape and the reading comes out cleaner.
Sharp and in focusA good sign: if you open the photo on your phone and read the small text comfortably, so does the recognition.
Even lightDiffuse ambient light gives the best result. A head-on flash bounces off the credential's glossy surface, so it helps to shoot from an angle where the phone doesn't cast its shadow.
On a flat surfaceResting flat rather than held in the air. A background in a different color helps tell the document's edges apart.
A direct photoThe original shot of the credential is the one that keeps the most detail. Screenshots, photos of a screen and filters hold on to less information.
Why we process imperfect photos
Raw infrastructure · 100% REST API

One call, one credit.

Same envelope as every OrigoID service: status, type, data. The front is required; send the back too if you want the keys and the cross-checks.

The address, solvedSeparate fields and the official catalog in the same response — no separate normalization provider.
Best-effort processingWe don't reject an image for not being perfect: it gets processed and you're told honestly what could be read and what couldn't.
Zero retentionImages are processed and discarded. No document database, in any region.
extrae-ine
const res = await fetch("https://api.origoid.com/mex/id/v1/voter-id-extractions", {
  method: "POST",
  headers: {
    "x-api-key": process.env.ORIGOID_API_KEY,
    "content-type": "application/json",
  },
  body: JSON.stringify({
    front: frontBase64,
    back: backBase64,   // optional: adds keys and cross-checks
  }),
});

const { data } = await res.json();
const dom = data.addressNormalized;
if (dom.geocodingStatus === "VERIFIED") saveToFile(dom);
import os, requests

res = requests.post(
    "https://api.origoid.com/mex/id/v1/voter-id-extractions",
    headers={
        "x-api-key": os.environ["ORIGOID_API_KEY"],
        "content-type": "application/json",
    },
    json={"front": front_base64, "back": back_base64},
    timeout=30,
)
data = res.json()["data"]
address = data["addressNormalized"]
curl -X POST https://api.origoid.com/mex/id/v1/voter-id-extractions \
  -H "x-api-key: $ORIGOID_API_KEY" \
  -H "content-type: application/json" \
  -d '{"front": "<base64>", "back": "<base64>"}'
The address, already broken apart
{
  "status": "OK",
  "type": "SUCCESS",
  "data": {
    "model": "E",
    "address": { "addressLine1": "AV LAGO ALBERTO 320 TORRE CENTRAL 503", … },
    "addressNormalized": {
      "geocodingStatus": "VERIFIED",
      "street": "Avenida Lago Alberto",
      "exteriorNumber": "320",
      "interiorNumber": "Torre Central 503",
      "neighborhood": "Anáhuac",
      "zipCode": "11320",
      "municipality": "Miguel Hidalgo",
      "state": "Ciudad de México"
    },
    "mrzValidation": { "cic": false, "name": true, … }
  },
  "billable": true
}
Every type, a decision

What your code does with each response.

typeWhat it meansDecision
SUCCESSThe document was read. Check geocodingStatus and the MRZ/QR flags to decide whether the file is complete.Proceed
IMAGE_UNREADABLEThe image is blurry or obstructed. Ask for just that photo again, with better light and no glare.Review
DOCUMENT_NOT_IDENTIFIEDWhat arrived isn't a recognized voter credential. Usually another ID or the wrong photo.Reject

Technical errors arrive separately with status: "ERROR": an image that isn't JPG/PNG, a file over 12 MB or a body past 28 MB are rejected as INVALID_REQUEST with billable: false — no charge. On the other hand, if the image was processed and came out unreadable, it did consume compute and is billed: we'd rather tell you now than surprise you on the invoice.

Real scope

What the OCR does not do.

A complete file is three calls.

The OCR digitizes the document and solves the address; the Lista Nominal confirms the credential is valid; face match confirms it's the person. Same envelope on all three.

See Validate INE
FAQ

What people usually ask us.

What exactly does it extract?

From the front: first and last names separately, date of birth, sex, CURP, elector key, the address and the holder's photograph. From the back: cic, ocr, citizenIdentifier, the MRZ and the QR depending on the model. Plus registration, emission and validity years, the electoral geography and the detected model (D, E, F, G, H or I).

How does the address arrive?

Twice over: the printed text as-is in address, and in addressNormalized split into street, exterior and interior number, neighborhood, ZIP, municipality, state and country, with abbreviations expanded and validated against the official catalog. The geocodingStatus tells you the match confidence: VERIFIED, PARTIAL or UNVERIFIED. These are address components, not coordinates.

Do I need to send the back?

It isn't required. With the front you already get the printed data and the photo. The back adds the keys (CIC, OCR, identifier) and enables the MRZ and QR cross-checks against what's printed.

Does it detect a tampered credential?

It flags inconsistencies. With the back, it contrasts what's printed against the MRZ and the QR and marks it field by field in mrzValidation and qrValidation. A false tells you what doesn't line up; it isn't a ruling of forgery by itself, it's the signal to review.

Do you store the images?

No. They're processed for the extraction and discarded on response: no document database. Zero retention across all our regions.

How much does it cost?

One extraction = 1 credit, whether you send just the front or front and back. You start free with courtesy credits, no card, and the price drops with your volume. All plans published.

Start free, no card.

Courtesy credits to try the OCR with real credentials in production. Access is enabled after a brief validation of your company.

Request access