Prerequisites
- An OkraPDF account (sign up)
- An API key (create one)
1. Upload a document
curl -X POST https://api.okra.app/v1/documents \
-H "Authorization: Bearer okra_YOUR_KEY" \
-F file=@invoice.pdf
{
"id": "doc-abc123",
"status": "queued"
}
curl -X POST https://api.okra.app/v1/documents/doc-abc123/upload-url \
-H "Authorization: Bearer okra_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/report.pdf"}'
2. Check status
curl https://api.okra.app/v1/documents/doc-abc123 \
-H "Authorization: Bearer okra_YOUR_KEY"
{
"id": "doc-abc123",
"status": "completed",
"file_name": "invoice.pdf",
"total_pages": 3,
"pages_completed": 3
}
3. Get results
# Full document as markdown
curl https://api.okra.app/v1/documents/doc-abc123/full.md
# Extracted tables
curl https://api.okra.app/v1/documents/doc-abc123/entities/tables
# Single table as CSV
curl "https://api.okra.app/v1/documents/doc-abc123/entities/tables/0?format=csv"
4. Chat with the document
curl -X POST https://api.okrapdf.com/document/doc-abc123/chat/completions \
-H "Authorization: Bearer okra_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"messages": [{"role": "user", "content": "What is the total amount?"}]}'
Choose a model
Override the default model with any OpenRouter model ID:curl -X POST https://api.okrapdf.com/document/doc-abc123/chat/completions \
-H "Authorization: Bearer okra_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"messages": [{"role": "user", "content": "What is the total amount?"}],
"agentConfigOverride": {
"llm": {"chat": {"provider": "openrouter", "model": "anthropic/claude-haiku-4.5"}}
}
}'
curl -X POST https://api.okrapdf.com/document/doc-abc123/chat/completions \
-H "Authorization: Bearer okra_YOUR_KEY" \
-H "Content-Type: application/json" \
-H 'X-Vendor-Keys: {"anthropic": "sk-ant-..."}' \
-d '{
"messages": [{"role": "user", "content": "What is the total amount?"}],
"agentConfigOverride": {
"llm": {"chat": {"provider": "anthropic", "model": "claude-haiku-4-5-20251001"}}
}
}'
Add custom instructions
Append domain context viasystemPromptSuffix:
curl -X POST https://api.okrapdf.com/document/doc-abc123/chat/completions \
-H "Authorization: Bearer okra_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"messages": [{"role": "user", "content": "What is total revenue?"}],
"systemPromptSuffix": "Amounts are in HKD millions. Respond in one sentence."
}'
Python example
import requests
import time
import os
API_KEY = os.environ["OKRA_API_KEY"]
BASE = "https://api.okra.app"
headers = {"Authorization": f"Bearer {API_KEY}"}
# Upload
with open("report.pdf", "rb") as f:
resp = requests.post(f"{BASE}/v1/documents", headers=headers, files={"file": f})
doc_id = resp.json()["id"]
# Poll
while True:
status = requests.get(f"{BASE}/v1/documents/{doc_id}", headers=headers).json()
if status["status"] in ("completed", "failed"):
break
print(f"Pages: {status.get('pages_completed', 0)}/{status.get('total_pages', '?')}")
time.sleep(2)
# Get markdown
md = requests.get(f"{BASE}/v1/documents/{doc_id}/full.md").text
print(md)
JavaScript example
const API_KEY = process.env.OKRA_API_KEY;
const BASE = "https://api.okra.app";
const headers = { Authorization: `Bearer ${API_KEY}` };
// Upload from URL
const { id } = await fetch(`${BASE}/v1/documents/${id}/upload-url`, {
method: "POST",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify({ url: "https://example.com/report.pdf" }),
}).then(r => r.json());
// Poll
let status;
do {
await new Promise(r => setTimeout(r, 2000));
status = await fetch(`${BASE}/v1/documents/${id}`, { headers }).then(r => r.json());
} while (!["completed", "failed"].includes(status.status));
// Get tables
const tables = await fetch(`${BASE}/v1/documents/${id}/entities/tables`).then(r => r.json());
console.log(`Extracted ${tables.nodes.length} tables`);