Accie OCR
REST API
API base URL: https://ocr.accie.ai. Paths are under /api/v1/. OpenAPI title Accie OCR API 0.1.0. Interactive spec: https://ocr.accie.ai/docs (Swagger) and /redoc.
Authentication
The API accepts, in order:
X-API-Key: accie_sk_…- or
Authorization: Bearer accie_sk_…(bearer that looks like an API key) - or
Authorization: Bearer <JWT>when OIDC is on (Accie access token — audiences include OCR / account portal per the skill file) - or
Authorization: Bearer <OCR session token>from the workspace cookie/session path
Org id always comes from the key or session — never from a client-supplied org field. Keys are org-scoped, hashed at rest, prefix accie_sk_. The raw secret is returned once on POST /api/v1/api-keys.
Create keys from the workspace (Settings → Integrations) while signed in at ocr.accie.ai. Password login/register on the OCR API is disabled when Identity is on (see 403 below).
Error shape
Missing auth — POST /api/v1/jobs and GET /api/v1/ocr-kinds both returned HTTP 401:
{"detail":{"error":"missing_bearer_token","message":"missing_bearer"}}
Password login while Identity-only — POST /api/v1/auth/login with email/password returned HTTP 403:
{"detail":{"code":"password_auth_disabled","message":"Password auth is disabled. Sign in with Accie Accounts."}}
Validation — POST /api/v1/auth/login with {} returned HTTP 422 FastAPI HTTPValidationError (array of loc / msg for missing email and password).
Rate limits return HTTP 429 with Retry-After and codes rate_limited_key, rate_limited_org, rate_limited_global, or rate_limited_ui. A saturated queue returns 503 queue_saturated.
Public reads
These four reads need no API key. Try one live, right from this page, against the production API:
Try it live — no key required
GET https://ocr.accie.ai/health → 200
{"ok":true,"service":"accie_ocr"}
GET /api/v1/export-formats → 200 (truncated here to lanes):
{
"lanes": {
"invoice": ["json", "csv", "xlsx", "tally", "gstin_pack"],
"form": ["json", "csv", "xlsx"],
"document": ["json", "csv", "xlsx", "txt", "md"],
"handwritten": ["json", "txt", "md"],
"id": ["json", "csv", "xlsx"]
},
"deferred": ["searchable_pdf", "docx"],
"max_pages_per_unit": 36
}
GET /api/v1/plans is public. The ocr_beta row reports pages_included 1000, storage_mb 10240, price_inr_monthly 0, billing_authority identity, gst_rate_percent 18. Accie Accounts signup shows OCR Beta as 1,000 pages · 5,120 MB · 14 days. Treat Accounts as the billing display — do not assume the two storage numbers match.
GET /api/v1/id/schemas is also public (200, no auth): government_verified: false, disclaimer “Soft validation only — format/checksum/heuristic checks. Not UIDAI, NSDL, Parivahan, NVSP, or DigiLocker authentication.”
Job lifecycle
POST /api/v1/jobswith at leastfilename. Response includesid,status, andupload_url(presigned PUT) per OpenAPIJobOut.- HTTP PUT the file bytes to
upload_url(not through MCP). - Poll
GET /api/v1/jobs/{id}untilready,needs_review, orfailed. POST /api/v1/jobs/{id}/exports/{fmt}— pages are not re-charged for export (product docs / skill).
Optional body fields from OpenAPI JobCreateIn: content_type, estimated_pages, template_id, prefer_gpu, document_class, options, tag, idempotency_key (also Idempotency-Key header). Replay returns the same job with replayed: true.
Required JobOut fields: id, status, filename, content_type, page_count, routing_tag, queue_name, result_version. Optional: upload_url, pages, extracted_fields, verification, via_api_key, batch_id.
Create a job — three languages
Replace $ACCIE_API_KEY with a secret from Settings → Integrations. Requests without a key return the 401 body above.
export ACCIE_API_URL=https://ocr.accie.ai
export ACCIE_API_KEY=accie_sk_your_key
# Without a key:
# {"detail":{"error":"missing_bearer_token","message":"missing_bearer"}}
curl -sS -X POST "$ACCIE_API_URL/api/v1/jobs" \
-H "Authorization: Bearer $ACCIE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"filename": "invoice.pdf",
"content_type": "application/pdf",
"estimated_pages": 1,
"document_class": "invoice"
}'
# PUT the file to the returned upload_url, then:
# curl -sS -H "Authorization: Bearer $ACCIE_API_KEY" \
# "$ACCIE_API_URL/api/v1/jobs/$JOB_ID"
import os
import httpx
base = os.environ["ACCIE_API_URL"].rstrip("/")
headers = {"Authorization": f"Bearer {os.environ['ACCIE_API_KEY']}"}
with httpx.Client(base_url=base, headers=headers, timeout=60.0) as client:
created = client.post(
"/api/v1/jobs",
json={
"filename": "invoice.pdf",
"content_type": "application/pdf",
"estimated_pages": 1,
"document_class": "invoice",
},
)
created.raise_for_status()
job = created.json()
# job["upload_url"] — PUT file bytes (presigned; may not need the API key)
status = client.get(f"/api/v1/jobs/{job['id']}")
print(status.json()["status"])
const base = process.env.ACCIE_API_URL; // https://ocr.accie.ai
const headers = {
Authorization: `Bearer ${process.env.ACCIE_API_KEY}`,
"Content-Type": "application/json",
};
const created = await fetch(`${base}/api/v1/jobs`, {
method: "POST",
headers,
body: JSON.stringify({
filename: "invoice.pdf",
content_type: "application/pdf",
estimated_pages: 1,
document_class: "invoice",
}),
});
if (!created.ok) {
throw new Error(await created.text());
}
const job = await created.json();
const status = await fetch(`${base}/api/v1/jobs/${job.id}`, { headers });
console.log((await status.json()).status);
X-API-Key is equivalent to a bearer API key.
Other authenticated routes
Batches /api/v1/batches, templates /api/v1/extraction-templates, usage /api/v1/usage, webhooks /api/v1/webhooks, me /api/v1/me, org settings /api/v1/org/settings. Full path list: https://ocr.accie.ai/openapi.json.