Common patterns

The examples below use an SDK client, client, or cURL with the Extract base URL. Use the generated OpenAPI document for route-specific statuses and schemas.

Waiting for a Run

Runs are asynchronous. The SDKs wait for a Run when they start it. For a Run started elsewhere, or with wait=False, wait() polls until finished is true and raises JobFailedError if the Run failed. With cURL, poll until finished is true, then read status for the outcome: completed, stopped, or failed.

run = client.extract.runs.wait(run_id, timeout=3600)  # polls with backoff
print(run.status)

Use a client-side timeout and a bounded polling interval. Completion time depends on source coverage, crawl scope, and queue state.

Handling errors

Check the status before decoding a success payload. API errors use the stable { "error": { "code", "message", "request_id" } } envelope; retain the request ID when contacting support.

from extralt import APIStatusError, RateLimitError

try:
    run = client.extract.runs.create(robot_id=robot_id, budget=100)
except RateLimitError:
    ...  # raised only after the SDK's own retries
except APIStatusError as error:
    print(error.status_code, error.code, error.request_id)

For create and restart requests, retry the same payload with the original Idempotency-Key; the SDKs do this for their own retries. This returns the original result instead of duplicating work. A key reused with a different payload returns 409 Conflict.

Pagination

List responses use cursor pagination. Pass a non-null next_cursor as the cursor query parameter for the next page.

captures = list(client.extract.captures.list(run_id=run_id))

Treat cursors as opaque. Do not decode, construct, or replace one with a resource id. Preserve the same endpoint, filters, and sort across pages; the next request may choose a different valid limit.

Pagination reads live data rather than holding a database transaction across HTTP requests. On unchanged data, keyset cursors traverse deterministically. Rows written between requests can affect the remaining pages, so restart without a cursor when you need a refreshed traversal.

Explore Analysis pages

Pageable Explore Analysis responses use the same data and next_cursor fields. Their summary, coverage, and freshness describe the complete selected Analysis scope, while data contains only the current evidence page. On unchanged data, those scope-level values repeat on later pages. A non-null next_cursor is the only continuation signal.

Timestamps

Every point in time, in requests and responses, is an RFC 3339 timestamp such as 2026-01-31T00:00:00Z. Explore time windows take from and to query parameters in that format; to is exclusive.

Rate-limit handling

A 429 response is authoritative. Honor Retry-After when present, then retry with exponential backoff, random jitter, and a maximum attempt count. The SDKs do this for every operation that is safe to repeat. Do not build against an observed request rate as if it were a permanent quota; operational limits can change.

Reads are generally safe to retry. Retry a mutation only with the same Idempotency-Key and payload, or when the operation is otherwise documented as idempotent.

Bulk Capture export

Use the Capture export route instead of paginating when you need all Captures from one Run or Import as JSONL or Parquet. Stream the response to disk rather than loading the entire file in memory. See Working with Captures.