OCR is the first step, not the final output
OCR recognizes document text. If you are evaluating an API for OCR, the question is rarely whether it reads characters; it is what your workflow can safely do with the result. Most business workflows need more: named fields, tables, totals, identities, document types, validation status, and an audit trail. Cogneris combines OCR, layout understanding, multimodal extraction, and deterministic validation in one API.
Recognize text, tables, layout, handwriting, and fields
Use Cogneris on native PDFs, scanned PDFs, mobile photos, receipt images, invoices, bank statements, contracts, IDs, and onboarding packets. The API preserves the layout context needed to understand labels, rows, columns, and page-level evidence.
Text and layout
Read text, page structure, bounding boxes, sections, and table boundaries.
Fields and tables
Return document-specific JSON rather than making your team parse raw OCR text.
Review and validation
Send uncertain fields to review and validate values before downstream export.
Turn OCR output into structured JSON
For receipts, invoices, and KYC documents, the Cogneris structured document extraction API can return normalized fields, arrays, dates, amounts, identifiers, and validation metadata. Read the extraction docs for request and response examples. When the workflow needs the document itself rather than fields, the same pipeline can return a searchable PDF with the OCR text layer kept behind the original scan.
Request snippet: OCR plus structured output
Teams comparing OCR APIs often start with text extraction, then add tables and JSON when the output reaches a product workflow. There is no client library to install — the call is a multipart POST with a bearer key, and the language support matrix says the same for every stack.
curl -X POST "$COGNERIS_BASE_URL/Document/extraction" \
-H "Authorization: Bearer $COGNERIS_API_KEY" \
-F "file=@./statement.pdf"
OCR API quickstart in Python
A Python OCR API call is the same request made with the standard requests library, so there is nothing to install beyond it. The response envelope carries the recognized text, the extracted fields, and per-field confidence. The same multipart POST works from Node.js, .NET, Java, or any HTTP client; the API examples show each one.
import os
import requests
with open("statement.pdf", "rb") as document:
response = requests.post(
f"{os.environ['COGNERIS_BASE_URL']}/Document/extraction",
headers={"Authorization": f"Bearer {os.environ['COGNERIS_API_KEY']}"},
files={"file": ("statement.pdf", document, "application/pdf")},
timeout=120,
)
envelope = response.json()
if not response.ok or envelope.get("hasErrors"):
raise RuntimeError(f"extraction failed with status {response.status_code}")
print(envelope["data"]["metadata"])
Step 2: act on confidence, field by field
Every extracted value arrives as a cell with its value and a confidence from 0 to 100. The useful pattern is not to trust or distrust the whole document but to let the high-confidence fields through and queue the rest for a person. The threshold is yours; 90 is a common starting point for financial documents.
fields = envelope["data"]["metadata"]
threshold = 90
accepted = {name: cell["value"] for name, cell in fields.items()
if isinstance(cell, dict) and cell.get("confidence", 0) >= threshold}
to_review = [name for name, cell in fields.items()
if isinstance(cell, dict) and cell.get("confidence", 0) < threshold]
print(accepted) # straight into your system
print(to_review) # the fields a reviewer should see, with page and bounding box
Step 3: long PDFs with the asynchronous job flow
A synchronous call is right for a one-page receipt. For a 40-page packet, upload the file once as an artifact, submit a job that references it, and poll until it finishes, waiting the Retry-After interval the API returns between calls. A stable Idempotency-Key on the submit means a retried request returns the same job instead of queuing a second one. Sandbox keys, the ones that start with xtkt_test_, run this flow without spending credits.
import time
base = os.environ["COGNERIS_BASE_URL"]
auth = {"Authorization": f"Bearer {os.environ['COGNERIS_API_KEY']}"}
with open("loan-packet.pdf", "rb") as document:
upload = requests.post(f"{base}/api/v1/artifacts", headers=auth,
files={"file": document}, timeout=120).json()
job = requests.post(f"{base}/api/v1/document-jobs", headers={**auth, "Idempotency-Key": "loan-2026-0001"},
json={"operation": "Extraction", "inputReference": upload["data"]["reference"]},
timeout=30)
job_id = job.json()["data"]["jobId"]
while True:
status = requests.get(f"{base}/api/v1/document-jobs/{job_id}", headers=auth, timeout=30)
state = status.json()["data"]["status"]
if state in ("Succeeded", "Failed", "Cancelled"):
break
time.sleep(int(status.headers.get("Retry-After", "5")))
if state == "Succeeded":
result = requests.get(f"{base}/api/v1/artifacts/content", headers=auth,
params={"reference": status.json()["data"]["outputReference"]}, timeout=120)
envelope = result.json()
Step 4: webhooks instead of polling
Polling is fine for a script and wrong for a service. Register an HTTPS endpoint with POST /api/v1/webhook-endpoints and the completion event comes to you. The delivery announces the result rather than carrying it, so your receiver reads the job and downloads the output exactly as in step 3. Every delivery is signed: the X-Xtrakt-Signature header carries t=<unix seconds>,v1=<hex>, where v1 is an HMAC-SHA256 of "<t>." + raw body keyed with the endpoint's whsec_ secret as the text it was shown to you, not decoded. Reject anything older than 300 seconds, and use the X-Xtrakt-Delivery id as the idempotency key, because retries reuse it. The retry schedule is in the API reference. The platform event webhooks that an MCP assistant registers under /api/v1/webhooks are a different contract, signed as Cogneris-Webhook-Signature with the secret base64url-decoded, so keep the two verifiers apart.
import hashlib, hmac, time
def verify(raw_body: bytes, signature_header: str, signing_secret: str) -> bool:
parts = dict(item.split("=", 1) for item in signature_header.split(","))
if abs(time.time() - int(parts["t"])) > 300:
return False
key = signing_secret.encode() # the whsec_ secret as text; do not base64-decode it
expected = hmac.new(key, parts["t"].encode() + b"." + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts["v1"])
Free plan limits
The quickstart above runs on the Free plan. The numbers that matter before you write more code:
| Limit | Free plan | What it means for an OCR test |
|---|---|---|
| Credits | 400, for 14 days, no card | A 1-page document classified and extracted uses 4.2 credits, so about 95 documents. |
| When credits run out | Calls stop | No overage and no surprise invoice; move to Pro (5,000 credits a month) when the test is done. |
| Sandbox keys | xtkt_test_ prefix | Run the asynchronous job flow without spending credits; the synchronous /Document/* routes need a production key. |
| Rate limit | Returns 429 as application/problem+json | Back off on the stable error code rather than on the status text. |
Current plan prices and credit rates are on the pricing page; the figures here are the ones it publishes today.
Confidence scores and bounding boxes
Every important value can carry confidence, source page, and bounding-box evidence. Reviewers can jump from JSON to the document region that produced the value, which makes QA and audit work much faster. Confidence is only half the picture: our document AI accuracy SLA sets out how accuracy is defined per field and measured against straight-through rates.
Selection marks, signatures, and handwriting
Production OCR often needs to understand more than printed text. Cogneris can preserve evidence around checkboxes, selection marks, signature presence, stamps, handwritten notes, and low-confidence handwriting so a reviewer can verify the field instead of re-reading the full document.
Multimodal extraction for hard documents
Some documents are too messy for text recognition alone: rotated scans, unusual templates, broken tables, photos with shadows, handwritten notes, and packets with mixed document types. Cogneris combines OCR with multimodal reasoning and validation rules so the output is built for decisions, not just search.
Agentic OCR for high-risk workflows
For checks, logistics packets, insurance forms, and finance documents, the OCR result is only the first pass. Cogneris can classify the document, extract the right schema, check totals or identifiers, and carry the confidence and evidence each value was read with, so a workflow can stop on the ones it should not trust. That loop is what agentic document extraction and document AI agents do for back-office work: classify, extract, validate, and hand off the cases they cannot settle.
Human review for uncertain fields
Every value comes back with a confidence score and the region it was read from, so you can decide which ones a person should see. Nothing escalates on the score by itself: review is a step you place in the workflow, and when the run reaches it the documents land in a review queue where a reviewer can correct a value before approving it. That keeps the workflow moving without pretending every OCR result is equally trustworthy.
OCR API pricing and workflow cost
Most OCR APIs look inexpensive when you compare only page recognition. The real cost appears when teams add custom parsers, table cleanup, confidence thresholds, review tools, and audit evidence. For buying criteria, see the document extraction API pricing guide and the extraction benchmark, or start with our guide to the best OCR APIs for developers.