Quickstart

Get a key, extract structured data from a PDF in one call, then scale it up.
View as Markdown

One POST turns a document into JSON with citations. This page gets you there, then shows the two moves you’ll need next: not blocking on long documents, and reusing a file you’ve already sent.

What you’ll need

  • A CloudRaker account with the admin role in your organization (creating keys is admin-only).
  • A terminal with curl.

Get a key

In the CloudRaker web app, open Admin → API keys and click Create API key. The full key value is shown once — copy it and store it somewhere safe. The walkthrough with screenshots is in API keys.

The plaintext key is returned only once and can never be retrieved again. If you lose it, revoke the key and create a new one.

$export CLOUDRAKER_API_KEY="sk_…"

Extract a document

The call below reads a blank IRS Form W-9 straight from a URL — no upload, no local file, nothing to set up. Replace the URL with your own document when you’re ready, or download the fictional sample invoice and upload it to try a business document.

$curl -X POST https://api.cloudraker.com/v1/extract \
> -H "Authorization: Bearer $CLOUDRAKER_API_KEY" \
> -H "Content-Type: application/json" \
> -d '{
> "file": { "url": "https://www.irs.gov/pub/irs-pdf/fw9.pdf", "name": "w9.pdf" },
> "schema": {
> "type": "object",
> "properties": {
> "business_name": { "type": ["string", "null"] },
> "tax_classification": { "type": ["string", "null"] }
> }
> }
> }'

The response is the finished run. Two things to read:

  • output.value — your data, shaped like your schema.
  • output.citations — for each field, which file, page, and region on the page it came from, so you can show a human the evidence. Page indexes are 0-based and boxes are normalized to the page (01, top-left origin).
1{
2 "object": "extract_run",
3 "id": "exr_01KYD1J8QW2RN4T6VXZ0ABCDEF",
4 "status": "processed",
5 "expiresAt": "2026-07-26T15:57:41.907Z",
6 "statusUrl": "/v1/runs/exr_01KYD1J8QW2RN4T6VXZ0ABCDEF",
7 "files": [{ "id": "b88ea8f9-20d4-4704-b379-ddee5a23c678", "name": "w9.pdf", "status": "processed" }],
8 "output": {
9 "value": { "business_name": null, "tax_classification": "Individual/sole proprietor or single-member LLC" },
10 "citations": {
11 "tax_classification": [
12 { "fileId": "b88ea8f9-20d4-4704-b379-ddee5a23c678", "page": 0, "bbox": { "x": 0.086, "y": 0.379, "width": 0.261, "height": 0.016 }, "text": "Individual/sole proprietor or single-member LLC", "confidence": 5 }
13 ]
14 },
15 "documents": [{ "fileId": "b88ea8f9-20d4-4704-b379-ddee5a23c678", "name": "w9.pdf", "status": "done", "value": { "business_name": null, "tax_classification": "Individual/sole proprietor or single-member LLC" } }]
16 }
17}

null means the field wasn’t in the document — which is why every field in the schema is declared nullable. That and the other four schema rules are in the extraction schema dialect.

Don’t block on slow documents

The call above is synchronous: it holds until the run finishes, up to 60 seconds by default and 120 at most (?wait=). When the cap is reached you don’t get a timeout error — you get a 202 with a handle to the same run:

1{
2 "object": "extract_run",
3 "id": "exr_01KYD1J8QW2RN4T6VXZ0ABCDEF",
4 "status": "processing",
5 "statusUrl": "/v1/runs/exr_01KYD1J8QW2RN4T6VXZ0ABCDEF"
6}

Long audio, hundred-page scans, and large batches will normally land here. If your caller can’t hold a request open, ask for the handle up front with ?wait=0:

$curl -X POST "https://api.cloudraker.com/v1/extract?wait=0" \
> -H "Authorization: Bearer $CLOUDRAKER_API_KEY" \
> -H "Content-Type: application/json" \
> -H "Idempotency-Key: invoice-4471" \
> -d '{ "file": { "url": "https://www.irs.gov/pub/irs-pdf/fw9.pdf" }, "schema": { "type": "object", "properties": { "business_name": { "type": ["string", "null"] } } } }'

Sending an Idempotency-Key makes retries safe: a replay returns the original run and an idempotent-replay: true response header instead of starting a second one.

Track a run

$curl "https://api.cloudraker.com/v1/runs/exr_01KYD1J8QW2RN4T6VXZ0ABCDEF?wait=30" \
> -H "Authorization: Bearer $CLOUDRAKER_API_KEY"

GET /v1/runs/:id returns the same run body as the original call, and takes the same ?wait= — so you can long-poll rather than hammering it. status moves through:

StatusMeaning
queuedAccepted, not started yet.
processingReading the document or running the capability.
processedFinished. output is present.
failedTerminal failure. Per-file causes are on files[].error.
cancelledYou cancelled it.
expiredPast its ttl — the run and its files are gone.
needs_inputWaiting on a human step. Not reachable for extract or parse.

Runs clean themselves up. ttl (seconds, default 24 hours, max 7 days) sets when the run and its files are purged; expiresAt on the run body tells you when. Cancelling, purging early, downloading a produced file, and keeping a result in the product are all on the Runs page.

Rather than polling at all, point webhook at your endpoint and get told when the run reaches a terminal state — see Webhooks.

Reuse a file

Every run returns file ids. Pass one back as {"id": …} and the document is not fetched or parsed again — the second extraction starts from the parse that already happened, which is both faster and cheaper than sending the URL twice.

$curl -X POST https://api.cloudraker.com/v1/extract \
> -H "Authorization: Bearer $CLOUDRAKER_API_KEY" \
> -H "Content-Type: application/json" \
> -d '{
> "file": { "id": "b88ea8f9-20d4-4704-b379-ddee5a23c678" },
> "schema": { "type": "object", "properties": { "requester_name": { "type": ["string", "null"] } } }
> }'

The same is true across capabilities: parse a document once, then run several narrow extractions against its file id. Send up to 100 files in one run with files: [...] instead of file.

File ids live as long as the run that created them. A DELETE on the run, or its ttl elapsing, removes the files too — so reuse them within the window, or set a longer ttl on the first call.

Where to go next