Jobs
List, poll, and fetch durable parse and extract work.
Every admitted parse, extract, or batch creates a durable job with an opj_… id scoped to your organization. Jobs survive client disconnects; cancellation is not part of API v1.
Request and response field definitions live in the Jobs API reference.
List jobs
GET /jobs returns lightweight summaries for top-level jobs. Batch children are omitted from the list; open the parent job to page through children. Results are newest-first with cursor pagination. Optional status and operation query parameters filter the page.
curl -s 'https://api.openparser.dev/jobs?status=succeeded&limit=25' \
-H 'Authorization: Bearer YOUR_API_KEY'
# Follow next_cursor for additional pages.
curl -s 'https://api.openparser.dev/jobs?cursor=CURSOR_FROM_PREVIOUS_PAGE' \
-H 'Authorization: Bearer YOUR_API_KEY'openparser jobs list --status succeeded --limit 25 --json
openparser jobs list --cursor CURSOR_FROM_PREVIOUS_PAGE --jsonconst page = await client.jobs.list({
status: 'succeeded',
limit: 25,
});
const next = page.next_cursor
? await client.jobs.list({ cursor: page.next_cursor })
: null;page = client.jobs.list(status="succeeded", limit=25)
if page.get("next_cursor"):
next_page = client.jobs.list(cursor=page["next_cursor"])Get a job
GET /jobs/{id} returns status, progress fields, and the terminal result when the job has finished. Batch parents include cursor-paginated child summaries; pass cursor and limit to walk children. Cross-tenant or unknown ids return 404.
curl -s 'https://api.openparser.dev/jobs/opj_...' \
-H 'Authorization: Bearer YOUR_API_KEY'
# Page batch children.
curl -s 'https://api.openparser.dev/jobs/opj_...?limit=10&cursor=CHILD_CURSOR' \
-H 'Authorization: Bearer YOUR_API_KEY'openparser jobs get opj_... --json
openparser jobs get opj_... --limit 10 --cursor CHILD_CURSOR --jsonconst job = await client.jobs.get('opj_...');
const batchPage = await client.jobs.get('opj_...', {
limit: 10,
cursor: 'CHILD_CURSOR',
});job = client.jobs.get("opj_...")
batch_page = client.jobs.get(
"opj_...",
limit=10,
cursor="CHILD_CURSOR",
)Get a parse result
For succeeded parse jobs, GET /jobs/{id}/result returns the selected wire representation (openparser@1 or raw) without re-running OCR. The format query parameter defaults to the job's original output_format. Extraction and batch outcomes are read from GET /jobs/{id} instead.
curl -s 'https://api.openparser.dev/jobs/opj_.../result?format=openparser@1' \
-H 'Authorization: Bearer YOUR_API_KEY'openparser jobs result opj_... --format openparser@1 --jsonconst parsed = await client.jobs.result('opj_...', {
format: 'openparser@1',
});parsed = client.jobs.result("opj_...", format="openparser@1")Download source bytes
When has_source is true on the job, GET /jobs/{id}/source streams the retained admission document. Jobs without retained source return 404 source_not_available.
curl -s 'https://api.openparser.dev/jobs/opj_.../source' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-o ./source.pdfopenparser jobs source opj_... -o ./source.pdfconst blob = await client.jobs.source('opj_...');
const bytes = Buffer.from(await blob.arrayBuffer());source_bytes = client.jobs.source("opj_...")Extraction review
Succeeded extraction jobs expose a versioned review resource. GET /jobs/{id}/review returns machine output, corrected output, status, and audit events. PATCH /jobs/{id}/review applies confirmations and retractions addressed by JSON Pointer with expected_version. A confirmation records what the field should say, signed by the caller; if that value differs from extraction, it is also the correction. A retraction takes back the caller's own current tip on that path. POST /jobs/{id}/review/complete approves or rejects the review. POST /jobs/{id}/review/reopen returns a completed review to pending; only the reviewer who signed the run off may reopen it.
curl -s 'https://api.openparser.dev/jobs/opj_.../review' \
-H 'Authorization: Bearer YOUR_API_KEY'
curl -s -X PATCH 'https://api.openparser.dev/jobs/opj_.../review' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{"expected_version":0,"confirmations":[{"path":"/invoice/total","value":120}]}'
curl -s -X POST 'https://api.openparser.dev/jobs/opj_.../review/complete' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{"expected_version":1,"status":"approved"}'openparser jobs review opj_... --json
openparser jobs review-update opj_... \
--expected-version 0 \
--confirm-json '[{"path":"/invoice/total","value":120}]' \
--json
openparser jobs review-complete opj_... \
--expected-version 1 \
--status approved \
--jsonconst review = await client.jobs.review('opj_...');
const updated = await client.jobs.updateReview('opj_...', {
expected_version: review.version,
confirmations: [{ path: '/invoice/total', value: 120 }],
});
const completed = await client.jobs.completeReview('opj_...', {
expected_version: updated.version,
status: 'approved',
});review = client.jobs.review("opj_...")
updated = client.jobs.update_review(
"opj_...",
{
"expected_version": review.version,
"confirmations": [{"path": "/invoice/total", "value": 120}],
},
)
completed = client.jobs.complete_review(
"opj_...",
{
"expected_version": updated.version,
"status": "approved",
},
)Poll until terminal
Poll status until it reaches a terminal value: succeeded, failed, or indeterminate. Async admissions return immediately with a job id (and often a Location header pointing at GET /jobs/{id}). Sync admissions that time out return 202 with the same job reference.
until STATUS=$(curl -s 'https://api.openparser.dev/jobs/opj_...' \
-H 'Authorization: Bearer YOUR_API_KEY' | jq -r .status) \
&& [[ "$STATUS" == "queued" || "$STATUS" == "running" ]]; do
sleep 2
done
echo "$STATUS"# Poll manually, or use sync admission helpers that block until ready.
openparser jobs get opj_... --jsonconst terminal = new Set(['succeeded', 'failed', 'indeterminate']);
let job = await client.jobs.get('opj_...');
while (!terminal.has(job.status)) {
await new Promise((resolve) => setTimeout(resolve, 2000));
job = await client.jobs.get('opj_...');
}job = client.wait_for_job("opj_...")