API Documentation

Extract structured passport data from MRZ images with a single API call.

Quick Start

No API key needed for up to 10 free requests per day. Just send an image:

curl -X POST https://passport-ocr.com/v1/ocr \
  -F "[email protected]"

API Collections

Import a pre-built collection into your favorite API client with all endpoints, variables, and example requests ready to go.

Authentication

For paid usage with unlimited requests, include your API key in one of these headers:

Authorization: Bearer passport_ocr_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
X-API-Key: passport_ocr_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

API keys are generated from your dashboard after purchasing credits. Keys use the format passport_ocr_<32-char-hex>.

Endpoints

EndpointMethodAuthDescription
/v1/ocrPOSTOptionalExtract MRZ data from a passport image
/v1/creditsGETAPI KeyCheck your credit balance
/v1/usageGETNoneCheck anonymous usage (based on fingerprint)
/v1/healthGETNoneHealth check and version info

Request Formats

The /v1/ocr endpoint accepts two formats. Maximum file size is 10 MB.

Multipart Form Data

POST /v1/ocr
Content-Type: multipart/form-data

Field: image (file)

Base64 JSON

POST /v1/ocr
Content-Type: application/json

{
  "image": "data:image/jpeg;base64,/9j/4AAQ..."
}

Response Format

Success Response

{
  "success": true,
  "data": {
    "documentType": "PASSPORT",
    "documentNumber": "AB1234567",
    "issuingCountry": "GBR",
    "lastName": "SMITH",
    "firstName": "JOHN",
    "nationality": "GBR",
    "dateOfBirth": "1990-01-15",
    "sex": "M",
    "expirationDate": "2030-06-20",
    "personalNumber": ""
  }
}

Error Response

{
  "success": false,
  "error": {
    "code": "MRZ_NOT_DETECTED",
    "message": "Could not detect an MRZ region in the uploaded image."
  }
}

Dry Run Mode

Send the X-Dry-Run: true header to validate your image and detect the MRZ region without performing full OCR. This is free and does not deduct any credits.

curl -X POST https://passport-ocr.com/v1/ocr \
  -H "X-Dry-Run: true" \
  -F "[email protected]"

Idempotency

For paid requests, include an Idempotency-Key header with a client-generated UUID. If you send the same key within 24 hours, you will receive the cached response without being charged again.

curl -X POST https://passport-ocr.com/v1/ocr \
  -H "Authorization: Bearer passport_ocr_xxx" \
  -H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
  -F "[email protected]"

Idempotency keys are scoped to your API key and cached for 24 hours. This feature is only available for authenticated (paid) requests.

Error Codes

CodeHTTPDescription
RATE_LIMIT_EXCEEDED429Anonymous daily limit reached
CAPACITY_EXCEEDED429Server at capacity (free tier)
INSUFFICIENT_CREDITS402No credits remaining
INVALID_API_KEY401API key not recognized
INVALID_IMAGE400Image could not be parsed
MRZ_NOT_DETECTED422MRZ region not found in image
OCR_FAILED422MRZ detected but text unreadable
MRZ_PARSE_FAILED422OCR text found but MRZ validation failed
FILE_TOO_LARGE413Image exceeds 10 MB limit
INTERNAL_ERROR500Unexpected server error

Code Examples

cURL

curl -X POST https://passport-ocr.com/v1/ocr \
  -H "Authorization: Bearer passport_ocr_xxx" \
  -F "[email protected]"

JavaScript (fetch)

const form = new FormData();
form.append("image", fileInput.files[0]);

const response = await fetch("https://passport-ocr.com/v1/ocr", {
  method: "POST",
  headers: {
    "Authorization": "Bearer passport_ocr_xxx",
  },
  body: form,
});

const result = await response.json();
console.log(result.data);

Python (requests)

import requests

response = requests.post(
    "https://passport-ocr.com/v1/ocr",
    headers={"Authorization": "Bearer passport_ocr_xxx"},
    files={"image": open("passport.jpg", "rb")},
)

result = response.json()
print(result["data"])

Go

package main

import (
    "bytes"
    "fmt"
    "io"
    "mime/multipart"
    "net/http"
    "os"
)

func main() {
    file, _ := os.Open("passport.jpg")
    defer file.Close()

    body := &bytes.Buffer{}
    writer := multipart.NewWriter(body)
    part, _ := writer.CreateFormFile("image", "passport.jpg")
    io.Copy(part, file)
    writer.Close()

    req, _ := http.NewRequest("POST", "https://passport-ocr.com/v1/ocr", body)
    req.Header.Set("Authorization", "Bearer passport_ocr_xxx")
    req.Header.Set("Content-Type", writer.FormDataContentType())

    resp, _ := http.DefaultClient.Do(req)
    defer resp.Body.Close()
    result, _ := io.ReadAll(resp.Body)
    fmt.Println(string(result))
}

PHP

$ch = curl_init("https://passport-ocr.com/v1/ocr");
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => ["Authorization: Bearer passport_ocr_xxx"],
    CURLOPT_POSTFIELDS => ["image" => new CURLFile("passport.jpg")],
    CURLOPT_RETURNTRANSFER => true,
]);

$result = json_decode(curl_exec($ch), true);
curl_close($ch);
print_r($result["data"]);

Ruby

require "net/http"
require "json"

uri = URI("https://passport-ocr.com/v1/ocr")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer passport_ocr_xxx"

form_data = [["image", File.open("passport.jpg")]]
req.set_form(form_data, "multipart/form-data")

res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
puts JSON.parse(res.body)["data"]

Java

import java.net.http.*;
import java.nio.file.Path;

var client = HttpClient.newHttpClient();
var body = HttpRequest.BodyPublishers.ofFile(Path.of("passport.jpg"));
var request = HttpRequest.newBuilder()
    .uri(URI.create("https://passport-ocr.com/v1/ocr"))
    .header("Authorization", "Bearer passport_ocr_xxx")
    .header("Content-Type", "image/jpeg")
    .POST(body)
    .build();

var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());

C#

using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer passport_ocr_xxx");

using var content = new MultipartFormDataContent();
content.Add(new StreamContent(File.OpenRead("passport.jpg")), "image", "passport.jpg");

var response = await client.PostAsync("https://passport-ocr.com/v1/ocr", content);
var result = await response.Content.ReadAsStringAsync();
Console.WriteLine(result);

n8n Workflows

Import these ready-made workflows into n8n to integrate Passport OCR into your automations. Copy the JSON below and paste it in n8n via Menu → Import from JSON.

The API key is pre-filled with PUT_YOUR_API_KEY_HERE which works for testing with anonymous rate limits (10/day). Replace it with your real key from the dashboard for production use.

1. Form Upload → Passport OCR

A web form where users upload a passport image. The workflow sends it to the Passport OCR API and returns the extracted MRZ data.

{
  "name": "Passport OCR — Form Upload",
  "nodes": [
    {
      "parameters": {
        "formTitle": "Passport OCR",
        "formDescription": "Upload a passport image to extract MRZ data",
        "formFields": {
          "values": [
            {
              "fieldLabel": "Passport_Image",
              "fieldType": "file",
              "requiredField": true,
              "acceptFileTypes": ".jpg,.jpeg,.png,.webp,.bmp,.tiff",
              "multipleFiles": false
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.formTrigger",
      "typeVersion": 2.2,
      "position": [
        0,
        208
      ],
      "id": "f145d1cf-7c80-4bd0-be1a-246241fad1e4",
      "name": "On form submission",
      "webhookId": "passport-ocr-form"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.passport-ocr.com/v1/ocr",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-API-Key",
              "value": "PUT_YOUR_API_KEY_HERE"
            }
          ]
        },
        "sendBody": true,
        "contentType": "multipart-form-data",
        "bodyParameters": {
          "parameters": [
            {
              "parameterType": "formBinaryData",
              "name": "image",
              "inputDataFieldName": "Passport_Image"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        288,
        208
      ],
      "id": "927d3f88-51f9-4bbf-abcd-09ffbe6c873f",
      "name": "Passport OCR API"
    }
  ],
  "connections": {
    "On form submission": {
      "main": [
        [
          {
            "node": "Passport OCR API",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "pinData": {},
  "tags": []
}

What this does

  1. Form Trigger — Renders a web form with a file upload field for the passport image
  2. HTTP Request — Sends the uploaded image as multipart form-data to /v1/ocr and returns the parsed MRZ JSON

2. Telegram Bot → Passport OCR

Users send a passport photo to your Telegram bot. The workflow extracts MRZ data and replies with the result.

{
  "name": "Passport OCR — Telegram Bot",
  "nodes": [
    {
      "parameters": {
        "updates": [
          "message"
        ],
        "additionalFields": {
          "download": true,
          "imageSize": "large"
        }
      },
      "type": "n8n-nodes-base.telegramTrigger",
      "typeVersion": 1.2,
      "position": [
        0,
        208
      ],
      "id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
      "name": "Telegram Trigger",
      "webhookId": "passport-ocr-telegram",
      "credentials": {
        "telegramApi": {
          "id": "PASTE_CREDENTIAL_ID",
          "name": "Telegram Bot API"
        }
      }
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.passport-ocr.com/v1/ocr",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-API-Key",
              "value": "PUT_YOUR_API_KEY_HERE"
            }
          ]
        },
        "sendBody": true,
        "contentType": "multipart-form-data",
        "bodyParameters": {
          "parameters": [
            {
              "parameterType": "formBinaryData",
              "name": "image",
              "inputDataFieldName": "data"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        288,
        208
      ],
      "id": "d4e5f6a7-b8c9-0123-defa-234567890123",
      "name": "Passport OCR API"
    },
    {
      "parameters": {
        "resource": "message",
        "operation": "sendMessage",
        "chatId": "={{ $('Telegram Trigger').item.json.message.chat.id }}",
        "text": "=Passport OCR Result:\n\nName: {{ $json.data.firstName }} {{ $json.data.lastName }}\nNationality: {{ $json.data.nationality }}\nDocument: {{ $json.data.documentNumber }}\nDate of Birth: {{ $json.data.dateOfBirth }}\nExpiry: {{ $json.data.expirationDate }}\nSex: {{ $json.data.sex }}",
        "additionalFields": {
          "parse_mode": "Markdown"
        }
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        576,
        208
      ],
      "id": "e5f6a7b8-c9d0-1234-efab-345678901234",
      "name": "Send OCR Result",
      "credentials": {
        "telegramApi": {
          "id": "PASTE_CREDENTIAL_ID",
          "name": "Telegram Bot API"
        }
      }
    }
  ],
  "connections": {
    "Telegram Trigger": {
      "main": [
        [
          {
            "node": "Passport OCR API",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Passport OCR API": {
      "main": [
        [
          {
            "node": "Send OCR Result",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "pinData": {},
  "tags": []
}

What this does

  1. Telegram Trigger — Listens for incoming photo messages and downloads the image
  2. HTTP Request — Sends the photo to /v1/ocr as multipart form-data
  3. Send Message — Replies with the extracted passport data (name, nationality, document number, dates)

After importing, connect your Telegram Bot API credentials (Settings → Credentials → Add "Telegram Bot API" with your bot token from @BotFather).

Rate Limits

Free (Anonymous)

10/day

Resets at midnight UTC. No signup required.

Paid (API Key)

Unlimited

Limited only by your credit balance. $0.01 per request.

See our pricing page for credit packages and details.