API Documentation

Introduction

The Extract Passport Data API lets you extract data from passports of any country programmatically. It uses artificial intelligence to process the images, extract every field on the data page, and validate the MRZ with the ICAO 9303 check digits.

Base URL: https://extractpassportdata.com/api/v1

For AI agents: /llms.txt is the service index, and /llms-full.txt carries the endpoints, fields, prices and FAQ in a single plain-text file.

Get your API key

Create your account right here, without leaving this page, and generate your API key in under a minute.

20 free tokens when you create your account: enough to test the API with your own documents before buying a first package.

Already have an account? Go to your API keys.

Or continue with

Authentication

Every API request must be authenticated with an API key. Include your API key in the X-API-Key header of each request.

curl https://extractpassportdata.com/api/v1/balance \
  -H "X-API-Key: pass_your_api_key"

You can create and manage your API keys from the dashboard.

Upload Methods

The API supports several ways to send images. The method is detected automatically from the Content-Type header.

MethodContent-TypeRecommended for
Recommended
Multipart
multipart/form-datacurl, Postman, HTML forms
Base64 JSONapplication/jsonJavaScript, mobile apps
URL Fetchapplication/jsonCloud-hosted images
Binarioimage/jpeg, image/png, image/webpCLI tools, streaming

Endpoints

POST/api/v1/extract

Extracts data from an image of the passport data page.

RecommendedMultipart Form-Data

Send files directly. Ideal for curl and Postman.

FieldTypeDescription
image_frontfilePassport data page (required)
image_backfileAdditional page (optional)
curl -X POST https://extractpassportdata.com/api/v1/extract \
  -H "X-API-Key: pass_your_api_key" \
  -F "image_front=@./passport.jpg" \
  -F "image_back=@./passport_extra.jpg"

Base64 JSON

Send base64-encoded images. The data-URL prefix is optional.

FieldTypeDescription
image_frontstringBase64 of the front image (required)
image_backstringBase64 of the back image (optional)
IMAGE_FRONT=$(base64 < ./passport.jpg | tr -d '\n')

printf '{"image_front": "%s"}' "$IMAGE_FRONT" | curl -X POST https://extractpassportdata.com/api/v1/extract \
  -H "X-API-Key: pass_your_api_key" \
  -H "Content-Type: application/json" \
  --data-binary @-

URL Fetch

Send URLs of hosted images. Only HTTPS URLs are allowed.

FieldTypeDescription
image_front_urlstringHTTPS URL of the front image (required)
image_back_urlstringHTTPS URL of the back image (optional)
curl -X POST https://extractpassportdata.com/api/v1/extract \
  -H "X-API-Key: pass_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "image_front_url": "https://storage.example.com/passport.jpg",
    "image_back_url": "https://storage.example.com/passport_extra.jpg"
  }'

Binary Upload

Send the raw image as the request body. Supports one image per request.

curl -X POST https://extractpassportdata.com/api/v1/extract \
  -H "X-API-Key: pass_your_api_key" \
  -H "Content-Type: image/jpeg" \
  --data-binary @./passport.jpg

Successful response

{
  "success": true,
  "extraction_id": "clx1234567890",
  "data": {
    "passportNumber": "G12345678",
    "surname": "GOMEZ VELAZQUEZ",
    "givenNames": "MARGARITA",
    "nationality": "MEXICANA",
    "dateOfBirth": "05/07/1980",
    "sex": "F",
    "placeOfBirth": "COAHUILA",
    "dateOfIssue": "05/07/2023",
    "dateOfExpiry": "05/07/2033",
    "issuingCountry": "MEX",
    "issuingAuthority": "SRE",
    "personalNumber": "GOVM800705MCLRLR01",
    "mrzLine1": "P<MEXGOMEZ<VELAZQUEZ<<MARGARITA<<<<<<<<<<<<<",
    "mrzLine2": "G123456786MEX8007050F3307054<<<<<<<<<<<<<<08"
  },
  "tokens_remaining": 19,
  "upload_method": "multipart"
}

Send to a destination (optional)

Add an X-Destination-Id header to have the data delivered to one of your destinations before you are answered. It is optional and per request: without it the response is exactly what it has always been, even if you have a default destination configured.

curl -X POST https://extractpassportdata.com/api/v1/extract \
  -H "X-API-Key: pass_your_api_key" \
  -H "X-Destination-Id: <your destination id>" \
  -F "image_front=@./passport.jpg"

The response reports how the hand-off went. It is attempted exactly once, with no queue and no retry: if it failed, the data is still in your response and what to do next is yours to decide.

{
  "success": true,
  "extraction_id": "...",
  "data": { ... },
  "delivery": {
    "destination_id": "...",
    "succeeded": true,
    "http_status": 200,
    "latency_ms": 312,
    "error_code": null
  }
}

If the destination does not exist, is not yours, or is paused, the request is refused with 400 DESTINATION_NOT_FOUND before a token is spent.

GET/api/v1/balance

Returns your account’s current token balance.

Example

curl https://extractpassportdata.com/api/v1/balance \
  -H "X-API-Key: pass_your_api_key"

Response

{
  "success": true,
  "balance": 20
}

Capture links and webhooks

Send your customer a single-use link: they photograph their document on their phone and the data lands in your destination (webhook, email or Telegram), without the image passing through your system. Available to every account (beta). Destinations are set up in the dashboard, under Destinations.

POST/api/v1/capture-links
curl -X POST https://extractpassportdata.com/api/v1/capture-links \
  -H "X-API-Key: pass_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "destinationId": "<id de tu destino>",
    "documentType": "passport",
    "reference": "reserva-8841",
    "requireBack": true
  }'
  • destinationId and documentType (ine | passport) are required. reference (up to 80 characters) travels with the delivery. requireBack also asks for the back. requesterName (up to 60) is the business name your customer sees.
  • Single use: 15 minutes to open it and, once opened, 15 minutes to capture.
  • The url is only returned in this response; treat it as a credential and keep it out of logs.
  • Creating the link is free. The capture spends 1 token, refunded if the extraction or the delivery fails.
  • Up to 20 live links at once (400 TOO_MANY_LIVE_LINKS). A missing or paused destination: 400 DESTINATION_NOT_FOUND.

Response (201)

{
  "success": true,
  "id": "clx_enlace_456",
  "url": "https://extractpassportdata.com/c/…",
  "reference": "reserva-8841",
  "documentType": "passport",
  "requireBack": true,
  "askGuestPersona": false,
  "expiresAt": "2026-09-30T18:15:00.000Z"
}

Webhooks: what your server receives

Every delivery to a webhook destination, whether from a capture link, from /api/v1/extract with X-Destination-Id or from the dashboard, is a signed POST. It is attempted once, follows no redirects and waits 10 seconds; answer 2xx.

POST /tu-webhook
Content-Type: application/json
X-Signature: sha256=5f2b…
X-Signature-Timestamp: 1790000000
X-Idempotency-Key: clx1234567890

{
  "version": "1",
  "event": "extraction.completed",
  "idempotencyKey": "clx1234567890",
  "documentType": "passport",
  "source": "capture-link",
  "captureLinkId": "clx_enlace_456",
  "reference": "reserva-8841",
  "extractedAt": "2026-09-30T18:05:02.000Z",
  "data": { "curp": "…", "nombre": "…" }
}

source is api, web or capture-link. image (front/back in base64) is only present if the destination has image delivery on. Use X-Idempotency-Key to drop duplicates.

Signature: X-Signature is sha256= plus the hex HMAC-SHA256, keyed with the destination secret (whsec_…), of X-Signature-Timestamp + "." + the raw body. Verify over the bytes received (not re-serialized JSON), compare in constant time and reject timestamps older than 5 minutes. The official SDKs do this for you.

import { createHmac, timingSafeEqual } from 'node:crypto'

function verify(secret, rawBody, signature, timestamp) {
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false
  const expected = 'sha256=' + createHmac('sha256', secret)
    .update(`${timestamp}.${rawBody}`)
    .digest('hex')
  const a = Buffer.from(expected)
  const b = Buffer.from(signature ?? '')
  return a.length === b.length && timingSafeEqual(a, b)
}

Error codes

CodeHTTPDescription
MISSING_API_KEY401No API key provided
INVALID_API_KEY401Invalid or revoked API key
MISSING_IMAGE400No front image provided
INVALID_IMAGE_FORMAT400Unsupported format (use JPEG, PNG, or WebP)
IMAGE_TOO_LARGE400Image exceeds 10 MB
INVALID_BASE64400Failed to decode base64
INVALID_MULTIPART400Failed to parse the multipart form
URL_FETCH_FAILED400Could not fetch the image from the URL
URL_INVALID400Invalid URL (must be HTTPS)
URL_BLOCKED400URL blocked for security reasons
URL_TIMEOUT400Timed out fetching the image (10s)
UNSUPPORTED_CONTENT_TYPE415Unsupported Content-Type
INSUFFICIENT_TOKENS402Insufficient token balance
RATE_LIMITED429Too many failed or in-flight requests per minute; successful extractions do not count (see the Retry-After header)
TOO_MANY_FAILED_EXTRACTIONS429Too many consecutive failed extractions; check your image quality
EXTRACTION_FAILED500Extraction error (token refunded)
INTERNAL_ERROR500Internal server error

Extracted fields

The API extracts the following fields from the passport data page:

passportNumber

Passport number

surname

Surname(s)

givenNames

Given names

nationality

Nationality

dateOfBirth

Date of birth

sex

Sex

placeOfBirth

Place of birth

dateOfIssue

Date of issue

dateOfExpiry

Date of expiry

issuingCountry

Issuing country (ICAO code)

issuingAuthority

Issuing authority

personalNumber

Personal number (if present)

mrzLine1

MRZ line 1

mrzLine2

MRZ line 2

Limits and quotas

  • Each extraction consumes 1 token
  • Maximum image size: 10MB
  • Supported formats: JPEG, PNG, WebP
  • URL-fetch timeout: 10 seconds
  • If an extraction fails because of a system error, the token is refunded automatically
  • Tokens never expire