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. "citations": true adds the evidence behind every field. This page gets you there. It then shows two moves you need next: how to avoid blocking on long documents, and how to reuse a file you already sent.

What you’ll need

  • A CloudRaker account with the admin role in your organization. Only admins can create keys.
  • A terminal with curl.

Get a key

In the CloudRaker web app, open Admin → API keys and click Create API key. The app shows the full key value once. Copy it and store it in a safe place. The walkthrough with screenshots is in API keys.

The API returns the plaintext key only once. You can never retrieve it 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 from a URL. There is no upload, no local file, and no setup. Replace the URL with your own document when 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" },
> "citations": true,
> "schema": {
> "type": "object",
> "properties": {
> "business_name": { "type": ["string", "null"] },
> "tax_classification": { "type": ["string", "null"] }
> }
> }
> }'

The response is the finished run. Read two things:

  • output.value — your data, shaped like your schema.
  • output.citations — for each field, the source file, page, and region on the page. Use this to show a human the evidence. Page indexes are 0-based. Boxes are normalized to the page (01, top-left origin).

Citations are an add-on. Turn them on per request with "citations": true. When off, the output has no citations key. When on, every filled field comes back cited or declared absent. A field the documents do not contain gets {"fileId": "…", "notFound": true} and a null value, never a guess.

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 "business_name": [
15 { "fileId": "b88ea8f9-20d4-4704-b379-ddee5a23c678", "notFound": true }
16 ]
17 },
18 "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" } }]
19 }
20}

null means the document does not contain the field. For this reason, declare every field in the schema nullable. This is also why business_name above comes back with a notFound citation, not a value. This rule 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=). At the cap you do not 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 usually land here. If your caller cannot hold a request open, ask for the handle immediately 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"] } } } }'

An Idempotency-Key makes retries safe. A replay returns the original run and an idempotent-replay: true response header. It does not start a second run.

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. It takes the same ?wait=, so you can long-poll instead of polling in a tight loop. 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 gives the time. The Runs page covers cancelling, purging early, downloading a produced file, and keeping a result in the product.

To avoid polling, point webhook at your endpoint. You get notified when the run reaches a terminal state. See Webhooks.

Reuse a file

Every run returns file ids. Pass one back as {"id": …}. The API does not fetch or parse the document again. The second extraction starts from the existing parse. This is 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"] } } }
> }'

This works 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 removes the files. So does the ttl elapsing. Reuse the ids inside the window, or set a longer ttl on the first call.

Where to go next