OCR
Extract text from a PDF or a document image, with no chat model involved. You pay the per-page fee and no token charge. To have a model answer questions about the document instead, attach the file to a Chat Completions request, as described in Read documents, where kenari reads it for any model as ordinary input tokens. This endpoint is the same paid reading as the ocr engine there.
POST /v1/ocr
Request
Section titled “Request”Send a JSON body with either file or reuse_id, never both.
| Field | Type | Required | Description |
|---|---|---|---|
file | object | one of file or reuse_id | The document to read. |
file.filename | string | yes, with file | The file name. It is echoed back as name. |
file.file_data | string | yes, with file | A base64 data URL: data:<type>;base64,<contents>. A plain http URL is not fetched. |
reuse_id | string | one of file or reuse_id | A reuse_id from an earlier reading on your account. Serves the stored text at no charge. |
engine | string | no | Only ocr is accepted. Any other value is refused. |
Accepted types are application/pdf, image/png, image/jpeg, image/webp, image/gif and image/tiff. Two limits apply and either can refuse first: a page limit per document that kenari sets, and a size limit of about 15 MB. A document over either limit is refused, not read and not charged. The refusal message names the page limit when that is the one hit.
Response
Section titled “Response”The response has the extracted text and what the reading cost. pages is the number of pages actually read, and the charge is computed from it. cost_micro_idr is the fee in micro-Rupiah (Rupiah times 1,000,000), and it is 0 when the request only reused a stored reading. content holds the extracted text. confidence and low_confidence mean the same as in a chat response with the ocr engine: a mean per-page score, which is null when the engine gives no score, and a warning flag, not a guarantee. reuse_id lets you read the same document again at no charge, from this endpoint or from a chat request with the ocr engine.
{ "id": "req_8f286cec-075b-4083", "pages": 3, "cost_micro_idr": 225000000, "name": "invoice.pdf", "hash": "sha256:a3b6919a...", "content": [{"type": "text", "text": "# INVOICE\n..."}], "confidence": 0.9892, "low_confidence": false, "reuse_id": "ocr_8f286cec-075b-4083-a462-fc89170feb4f"}reuse_id is present when the reading was stored. Sending the same document again on the same account returns the stored reading and charges nothing, as long as the first reading was stored and carried a reuse_id. Treat any figure read from a scan as unverified until a person has checked it.
Examples
Section titled “Examples”Replace invoice.pdf with your own file. Each example encodes it as base64 first, and the curl example needs jq.
{ printf 'data:application/pdf;base64,'; base64 < invoice.pdf | tr -d '\n'; } > invoice.dataurl
jq -n --rawfile data invoice.dataurl '{file: {filename: "invoice.pdf", file_data: $data}}' \ | curl https://kenari.id/v1/ocr \ -H "Authorization: Bearer $KENARI_API_KEY" \ -H "Content-Type: application/json" \ -d @-To read again without paying, send the reuse_id instead of the file:
curl https://kenari.id/v1/ocr \ -H "Authorization: Bearer $KENARI_API_KEY" \ -H "Content-Type: application/json" \ -d '{"reuse_id": "ocr_8f286cec-075b-4083-a462-fc89170feb4f"}'Python
Section titled “Python”import base64import osimport requests
with open("invoice.pdf", "rb") as f: encoded = base64.b64encode(f.read()).decode()
response = requests.post( "https://kenari.id/v1/ocr", headers={"Authorization": f"Bearer {os.environ['KENARI_API_KEY']}"}, json={ "file": { "filename": "invoice.pdf", "file_data": f"data:application/pdf;base64,{encoded}", } },)response.raise_for_status()result = response.json()
print(result["pages"], result["cost_micro_idr"])print(result["content"][0]["text"])JavaScript
Section titled “JavaScript”import { readFile } from "node:fs/promises";
const encoded = (await readFile("invoice.pdf")).toString("base64");
const response = await fetch("https://kenari.id/v1/ocr", { method: "POST", headers: { Authorization: `Bearer ${process.env.KENARI_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ file: { filename: "invoice.pdf", file_data: `data:application/pdf;base64,${encoded}`, }, }),});if (!response.ok) throw new Error(await response.text());const result = await response.json();
console.log(result.pages, result.cost_micro_idr);console.log(result.content[0].text);Billing
Section titled “Billing”Reading is billed per page read, from your balance. Plans do not cover it. kenari checks that your balance can cover the document before it reads anything. If it cannot, the request is refused with 402 and nothing is charged, and a failed reading is not charged either. A reuse is free. See How billing works for balance and price dimensions.
Errors
Section titled “Errors”| Status | Code | When |
|---|---|---|
| 400 | bad_request | The body has neither file nor reuse_id, or both. file_data is not a base64 data URL, is empty, or is not valid base64. The type is not accepted. The document is over the size limit or the page limit. engine is not ocr. The reuse_id is unknown for your account. |
| 400 | bad_request | The key is restricted to certain models and was not granted Document reading, or document reading is switched off. A shared API key sent a reuse_id that it did not create. |
| 402 | insufficient_balance | Your balance cannot cover the document. The message says that document reading is billed per page from your balance and that plans do not cover it, and it suggests sending the file to a model that reads files. It shows the balance available and the most this document can reserve. |
| 503 | upstream_error | The reading failed. You are not charged. Retry. |
A body that is not valid JSON, a request without Content-Type: application/json, and a file object with a missing or wrong-typed filename or file_data are rejected before the endpoint runs, with a plain-text body and no code: 400 for invalid JSON, 415 for the missing content type, and 422 for a missing or wrong-typed field. See Errors.
A key restricted to certain models needs Document reading ticked under Paid capabilities. See Authentication and API keys. See Errors for the other codes.