Documentation /docs/async-jobs

Async Jobs

Most heavy production workloads should use execution_mode: async. The pattern is the same across parse, OCR, layout, tables, schema builder, extract, classify, split, equations, charts, and Ask where the endpoint supports async mode:

  1. Send the original request with execution_mode: "async"
  2. Store the returned job_id
  3. Poll GET /v1/jobs/{job_id}
  4. Fetch GET /v1/jobs/{job_id}/result once the job status is completed

Status vocabulary

Job status is one of:

  • pending: request accepted and waiting for a worker
  • processing: worker has started endpoint execution
  • completed: result is ready at GET /v1/jobs/{job_id}/result
  • failed: job reached a terminal failure; inspect the result/error payload before retrying

Treat any undocumented status as a contract mismatch and report it with the job ID.

State machine

pending -> processing -> completed
pending -> processing -> failed
pending -> failed

Poll with exponential backoff. Start with 2 seconds between polls for interactive flows, then increase the interval for batch workloads. Do not tight-loop the status endpoint.

cURL

JOB_ID=$(
  curl -sS -X POST "https://api.docspeed.ai/api/v1/parse" \
    -H "Authorization: Bearer ${DOCSPEED_API_KEY}" \
    -H "Content-Type: application/json" \
    -d '{
      "input": {"file_id": "file_123"},
      "execution_mode": "async",
      "profile": "grounded",
      "grounding": "regions"
    }' | jq -r '.job_id'
)

curl -sS \
  -H "Authorization: Bearer ${DOCSPEED_API_KEY}" \
  "https://api.docspeed.ai/v1/jobs/${JOB_ID}"

Python

import requests

submit = requests.post(
    "https://api.docspeed.ai/api/v1/parse",
    headers={
        "Authorization": "Bearer YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={
        "input": {"file_id": "file_123"},
        "execution_mode": "async",
        "profile": "grounded",
        "grounding": "regions",
    },
    timeout=600,
)
submit.raise_for_status()
job_id = submit.json()["job_id"]

status = requests.get(
    f"https://api.docspeed.ai/v1/jobs/{job_id}",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
    timeout=60,
)
print(status.json())

TypeScript

const submit = await fetch("https://api.docspeed.ai/api/v1/parse", {
  method: "POST",
  headers: {
    Authorization: "Bearer YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    input: { file_id: "file_123" },
    execution_mode: "async",
    profile: "grounded",
    grounding: "regions",
  }),
});

const { job_id } = await submit.json();
const status = await fetch(`https://api.docspeed.ai/v1/jobs/${job_id}`, {
  headers: { Authorization: "Bearer YOUR_API_KEY" },
});

console.log(await status.json());

Example job result

{
  "job_id": "job_123",
  "operation": "parse",
  "status": "completed",
  "execution_mode": "async",
  "created_at": 1776054000.0,
  "updated_at": 1776054018.0
}

Fetch the final payload

curl -sS \
  -H "Authorization: Bearer ${DOCSPEED_API_KEY}" \
  "https://api.docspeed.ai/v1/jobs/${JOB_ID}/result"

The result endpoint returns the same success payload that the original endpoint would have returned in sync mode. For example, async Extract returns the ExtractionResult envelope with schema, result, grounding_mode, and page_count.

If the job failed, the result endpoint may return the captured error body instead of a success payload. Handle it with the same logic described in Error Handling.