Quickstart

One endpoint, OpenAI-compatible request and response envelope. Use any OpenAI SDK with a different base URL. Base URL: https://api.dokumentli.ai/v1

Extract data from a document

Send the pages followed by one text part that contains the spec as a JSON string. A PDF goes in as a file part, images (JPEG or PNG, one per page, in reading order) as image_url parts. Both must be base64 data: URLs. Remote URLs are not accepted. Streaming is not supported.

curl https://api.dokumentli.ai/v1/chat/completions \
  -H "Authorization: Bearer $DOKUMENTLI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "dokumentli",
    "messages": [{
      "role": "user",
      "content": [
        {"type": "file", "file": {"filename": "invoice.pdf", "file_data":
          "data:application/pdf;base64,JVBERi0xLjc..."}},
        {"type": "text", "text": "{\"type\":\"extraction\",\"items\":{\"invoice_number\":{\"type\":\"string\"},\"invoice_date\":{\"type\":\"date\"},\"total_amount\":{\"type\":\"float\"}}}"}
      ]
    }]
  }'
from openai import OpenAI
import base64, json

client = OpenAI(
    base_url="https://api.dokumentli.ai/v1",
    api_key="dk_live_...",
)

spec = {"type": "extraction", "items": {
    "invoice_number": {"type": "string"},
    "invoice_date":   {"type": "date"},
    "total_amount":   {"type": "float"},
}}
pdf = base64.b64encode(open("invoice.pdf", "rb").read()).decode()

r = client.chat.completions.create(
    model="dokumentli",
    messages=[{"role": "user", "content": [
        {"type": "file", "file": {
            "filename": "invoice.pdf",
            "file_data": f"data:application/pdf;base64,{pdf}"}},
        {"type": "text", "text": json.dumps(spec)},
    ]}],
)
print(json.loads(r.choices[0].message.content))
import OpenAI from "openai";
import fs from "node:fs";

const client = new OpenAI({
  baseURL: "https://api.dokumentli.ai/v1",
  apiKey: process.env.DOKUMENTLI_API_KEY,
});

const spec = { type: "extraction", items: {
  invoice_number: { type: "string" },
  invoice_date:   { type: "date" },
  total_amount:   { type: "float" },
}};
const pdf = fs.readFileSync("invoice.pdf").toString("base64");

const r = await client.chat.completions.create({
  model: "dokumentli",
  messages: [{ role: "user", content: [
    { type: "file", file: {
        filename: "invoice.pdf",
        file_data: `data:application/pdf;base64,${pdf}` } },
    { type: "text", text: JSON.stringify(spec) },
  ]}],
});
console.log(JSON.parse(r.choices[0].message.content));

Response

The result is the JSON string in choices[0].message.content. Field values are always strings; an empty string means not found. usage.pages and usage.cost_eur show what was billed.

HTTP/1.1 200 OK
x-ratelimit-limit-pages: 20
x-ratelimit-remaining-pages: 18
x-dokumentli-credit-remaining: 49.95

{
  "id": "chatcmpl-9f2c...",
  "model": "dokumentli",
  "choices": [{
    "index": 0,
    "finish_reason": "stop",
    "message": {"role": "assistant", "content":
      "{\"invoice_number\":\"RE-2026-0412\",\"invoice_date\":\"2026-11-03\",\"total_amount\":\"1284.50\"}"}
  }],
  "usage": {"prompt_tokens": 1234, "completion_tokens": 56, "total_tokens": 1290,
            "pages": 2, "cost_eur": 0.05}
}

The full spec reference (groups, field types, guidance) is at dokumentli-docs.parashift.io.

Endpoints

MethodPathPurpose
POST/v1/chat/completionsSpec and pages in, structured JSON out.
GET/v1/modelsLists available models (dokumentli).
GET/v1/usageCredit balance, tier and limits of your organisation.

Billing and limits

One page is one PDF page or one image. Only successful requests (status 200) are billed. A request may contain up to 50 pages and 50 MB.

Limits apply per organisation: requests per minute, pages per minute and concurrent requests. Every response carries x-ratelimit-* headers. A 429 response includes Retry-After.

Errors

StatusCodeMeaningBilled
400invalid_request_errorMalformed request or specNo
401invalid_api_keyMissing, wrong or revoked keyNo
402insufficient_creditBalance too low for this requestNo
413request_too_largeToo many pages or bytes in one requestNo
429rate_limit_exceededSlow down, see Retry-AfterNo
502api_errorModel unreachable or unusable output, retry with backoffNo
503overloadedCapacity is saturated, retry with backoffNo
200Success. Unreadable documents return empty values.Yes