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.