← Home

OCR Callbacks

The /ocr and /ocr.json endpoints can deliver the OCR result to a callback URL.

Send the URL with the request. The endpoint returns HTTP 202 at once. OCRskill then sends the result with PUT.

A callback URL requires a paid API key. A free key returns 403.

Endpoints

POST https://api.ocrskill.com/ocr
POST https://api.ocrskill.com/ocr.json

Each request requires a bearer token.

Authorization: Bearer sk-your-key-here

Quick start

Set the X-OCR-Callback-URL header. Then upload a file.

curl "https://api.ocrskill.com/ocr" \
  -H "Authorization: Bearer sk-your-key-here" \
  -H "X-OCR-Callback-URL: https://uploads.example.com/result?signature=abc" \
  -F "file=@invoice.pdf"

The endpoint returns this body with status 202.

{
  "status": "accepted"
}

This body does not contain the OCR text. You can close the connection after you read this response.

OCRskill reads the file after it sends 202. It then sends PUT to the callback URL.

For this upload, the PUT body is Markdown. The Content-Type header is text/plain; charset=utf-8.

Send the callback URL

You can send the callback URL in the header or in the body. A body value replaces the header.

Request header

The header name is X-OCR-Callback-URL. Use this header for every upload type, including a raw file.

curl "https://api.ocrskill.com/ocr" \
  -H "Authorization: Bearer sk-your-key-here" \
  -H "Content-Type: application/pdf" \
  -H "X-OCR-Callback-URL: https://uploads.example.com/result" \
  --data-binary "@invoice.pdf"

Multipart form field

Send callback_url as a text field with the file.

curl "https://api.ocrskill.com/ocr" \
  -H "Authorization: Bearer sk-your-key-here" \
  -F "callback_url=https://uploads.example.com/result" \
  -F "file=@invoice.pdf"

JSON body

Send callback_url next to document_url. Use a public HTTP or HTTPS URL for the file.

curl "https://api.ocrskill.com/ocr" \
  -H "Authorization: Bearer sk-your-key-here" \
  -H "Content-Type: application/json" \
  -d '{
    "document_url": "https://cdn.example.com/invoice.pdf",
    "callback_url": "https://uploads.example.com/result"
  }'

When both values are present, OCRskill uses the body value.

Python

Put a public file URL in document_url. Then POST the JSON body to /ocr.

import json
import urllib.request
body = json.dumps({
    "document_url": "https://cdn.example.com/invoice.pdf",
    "callback_url": "https://uploads.example.com/result",
}).encode("utf-8")
request = urllib.request.Request(
    "https://api.ocrskill.com/ocr",
    data=body,
    headers={
        "Authorization": "Bearer sk-your-key-here",
        "Content-Type": "application/json",
    },
    method="POST",
)
with urllib.request.urlopen(request, timeout=30) as response:
    print(response.status)
    print(response.read().decode("utf-8"))

The status is 202. The body is {"status": "accepted"}.

POST the same JSON body to /ocr.json when you want typed fields. Put the field names in the query string.

import json
import urllib.request
body = json.dumps({
    "document_url": "https://cdn.example.com/invoice.pdf",
    "callback_url": "https://uploads.example.com/invoice.json",
}).encode("utf-8")
request = urllib.request.Request(
    "https://api.ocrskill.com/ocr.json?fields=company_name,invoice_date",
    data=body,
    headers={
        "Authorization": "Bearer sk-your-key-here",
        "Content-Type": "application/json",
    },
    method="POST",
)
with urllib.request.urlopen(request, timeout=30) as response:
    print(response.status)
    print(response.read().decode("utf-8"))

Node.js

Send the same JSON body with fetch. Use Node.js 18 or newer.

const response = await fetch("https://api.ocrskill.com/ocr", {
  method: "POST",
  headers: {
    Authorization: "Bearer sk-your-key-here",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    document_url: "https://cdn.example.com/invoice.pdf",
    callback_url: "https://uploads.example.com/result",
  }),
});
console.log(response.status);
console.log(await response.json());

Use /ocr.json and a fields query for typed JSON. The request body stays the same.

const response = await fetch(
  "https://api.ocrskill.com/ocr.json?fields=company_name,invoice_date",
  {
    method: "POST",
    headers: {
      Authorization: "Bearer sk-your-key-here",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      document_url: "https://cdn.example.com/invoice.pdf",
      callback_url: "https://uploads.example.com/invoice.json",
    }),
  },
);
console.log(response.status);
console.log(await response.json());

Read response.status before you use the body. A 202 status means OCRskill queued the callback.

Callback URL rules

Use an HTTPS URL. You can add a query string.

Do not put a user name or a password in the URL. Do not add a fragment (#).

The host must resolve to a public address. A public IPv4 address and a public IPv6 address both work.

OCRskill rejects a loopback address, a private address, and a link-local address.

A presigned upload URL works. Keep its signature in the query string. OCRskill does not add an auth header to the PUT.

What the PUT contains

OCRskill sends one PUT request. It sends Content-Type and the result body.

It does not send your API key. It does not follow a redirect.

The request timeout is 30 seconds. The connect timeout is 5 seconds.

The body matches a direct call to the same endpoint.

Markdown from /ocr

A multipart upload or a raw file produces Markdown.

Content-Type: text/plain; charset=utf-8

# Invoice

Total: 120.00

JSON document from /ocr

Send a JSON body with document_url. The callback body then uses this shape.

{
  "pages": [
    {
      "index": 0,
      "markdown": "# Invoice\n\nTotal: 120.00",
      "images": []
    }
  ],
  "model": "LightOnOCR-2-1B",
  "document_annotation": null,
  "usage_info": {
    "pages_processed": 1,
    "doc_size_bytes": 128
  }
}

The pages array holds one object per page. usage_info.pages_processed counts those pages.

doc_size_bytes is the character length of the data URI or document URL.

Typed JSON from /ocr.json

/ocr.json PUTs the same JSON as a direct call. The fields query still selects the fields.

curl "https://api.ocrskill.com/ocr.json?fields=company_name,invoice_date" \
  -H "Authorization: Bearer sk-your-key-here" \
  -H "X-OCR-Callback-URL: https://uploads.example.com/invoice.json" \
  -F "file=@invoice.pdf"

OCRskill sends this body with Content-Type: application/json.

{
  "company_name": "Example GmbH",
  "invoice_date": "2024-03-01"
}

Field names and error cases are in the Structured Data Extraction API.

Retries

OCRskill retries a PUT that fails.

A failure is a timeout, a connection error, or a status outside 2xx. A redirect status is also a failure.

OCRskill tries the PUT 21 times. It waits 3 minutes between tries.

A 2xx status stops the retries. After the last failed try, OCRskill discards the result.

Errors returned at once

OCRskill checks the request before it returns 202. These errors mean that OCRskill did not queue a callback.

Access

Situation Status Notes
Missing or invalid bearer token 401 Send Authorization: Bearer sk-....
Free API key 403 Callback URLs require a paid API key.

Callback URL

Situation Status Notes
URL scheme is not HTTPS 400 Use an HTTPS URL.
URL contains credentials or a fragment 400 Remove the user, the password, and the # fragment.
Host is not a public address 400 Use a public IP address.
Invalid URL or port 400 Check the host and the port.

Request body

Situation Status Notes
callback_url is not a string 400 Send text in the form field or the JSON field.
Unknown fields value on /ocr.json 400 See the field list.
Document URL is not public 400 OCRskill checks the document URL before the callback. See document URLs.

Errors after accept

A 202 response means OCRskill accepted the request. It does not mean that OCR finished.

If OCR fails after 202, OCRskill does not PUT an error body. Your server gets no callback for that request.

Your server also gets no successful PUT when every delivery try fails.

Skip the callback

Omit the callback URL to get the result in the HTTP response.

/ocr then returns Markdown, or the JSON document shape when the input is JSON. /ocr.json returns the typed fields.

Receive the PUT

Your URL must accept PUT. Return a status from 200 to 299.

Do not redirect that request. OCRskill reads the status code only.

Use a different path or query value for each file. OCRskill does not add a request id.

Frequently asked questions

Does a free API key work?

No. A free key returns 403. The message is Callback URLs require a paid API key.

Which callback URL does OCRskill use?

The body field callback_url replaces the header X-OCR-Callback-URL.

Can the source be a public URL?

Yes. Put an HTTP or HTTPS URL in document_url. A paid key is required.

Read the document URL reference.

Can I upload a raw file?

Yes. Put the callback URL in the X-OCR-Callback-URL header. The raw body is the file, so it cannot also hold callback_url.

What does /ocr.json send?

It sends the same JSON as a direct /ocr.json call. Read the field reference for the field names.

Does the PUT include page counts?

No. A direct response can include the X-OCR-Pages header. The PUT sends Content-Type and the body.

How long can delivery take?

Each wait is 3 minutes. There are 20 waits after the first try. The total wait is 60 minutes.