Skip to main content
Some capabilities take minutes, for example web.research, web.crawl, video.generate and browser.task.run. Their reference page says Mode: async. Calling one starts a job:

Wait for a job

Poll GET /v1/jobs/{id} until status is no longer running: The SDK does the polling for you, starting at 2 seconds and backing off to 30:
Instead of polling, you can subscribe to the job.completed webhook.

Bulk runs

A capability whose id ends in -bulk runs one lookup for many rows as one job. Give every row your own id; the result has one entry per row, in the order sent, keyed by that id:
  • found: false with no error means there is no record for that row. With an error the row could not be answered: it is free, and you can send it again later.
  • While the job runs, GET /v1/jobs/{id} may carry progress: { "total": 3, "done": 1 }.
  • If you cancel, the rows already answered stay on the job as its result, and those are the rows you pay for.
  • A run in which no row could be answered fails and is free.

Limits and charges

  • A job that runs longer than 30 minutes fails with upstream_timeout. A few capabilities allow longer, up to 24 hours.
  • To see what a job would cost before starting it, ask for an estimate.
  • Before a job starts, the most it can cost is set aside from your credit (a hold). If your balance or your daily spend cap cannot cover it, the request is refused with 402 spend_cap_reached and nothing runs. The error’s reason is balance (top up) or daily_cap (raise your daily cap), and required_micro is the amount needed. The hold is not a charge: when the job ends you pay its real price and the rest comes back. GET /v1/balance shows what is currently set aside as held_micro.
  • The start answer carries held_micro: the credit set aside for that job (0 when it takes none). It is never a price — the price is not known until the job is settled.
  • A job view answers with its own money. charged_micro is what the job has been charged so far: 0 while it runs, its price once it is settled, and 0 again for a job that failed or was refused and charged nothing. held_micro is what is still set aside for it: its estimate while it runs, 0 once it has finished. That is one job’s own charge, which the per-day, per-capability usage totals do not break down. A long outcome run keeps its money in its result’s cost, and its job view carries neither field.
  • A job that finishes with done is charged its real price, and what was set aside comes back. Where the data source stated its worst case for the request up front, the price is never more than the amount set aside. A failed job is free.
  • A job you cancel is charged for the work already done, where there was any, and never more than the amount set aside. Cancelling before any work was done is free.
  • Results are kept for 7 days.
  • Sending the same Idempotency-Key again returns the same job_id instead of starting a new job (see idempotency).