Documentation menu

Records now or a job later

Both routes take the same body, run the same request and produce the same records. They differ in when you get the records back.

Wait for the response

POST /records/{site} runs the request while the connection is open and answers with every record it produced, up to 500. Ask for more with maxItems and the API refuses with 413 and the message sync POST /records/amazon is capped at 500 items (requested 5000); use POST /jobs/amazon for larger runs. Leave maxItems out and the source default of 100 records applies.

The response carries records, count, stats and a jobId: the run is a job like any other, so GET /jobs/{id} shows it afterwards. 200 means the run finished. 499 means the job was cancelled before it finished and 500 means the run failed; both come with ok: false, the records written so far and error filled in.

Submit a job

POST /jobs/{site} answers 202 at once with the job's id and status. Poll GET /jobs/{id}, then read the records in pages from GET /jobs/{id}/records, or ask for a webhook. A job takes up to 5,000 records per submission; a larger maxItems is clamped, never refused, and the value in effect is the maxItems field of the job.

Side by side

POST /records/{site}POST /jobs/{site}
ReplyAfter the run finishesAt once, with 202 and the job
Records per requestUp to 500Up to 5,000, or the largest run your key allows
Where the records arriveIn the response bodyGET /jobs/{id}/records, in pages or as NDJSON
The connectionStays open for the whole runCloses as soon as the job is accepted
idempotency-keySupportedSupported
webhookUrlSupportedSupported
When the run fails499 or 500, with the records written so farThe job ends failed or cancelled; its records stay readable
Use it forLookups, tests, small batchesLarge runs and background work

The record envelope

Every record starts with the same fields, whatever the source, followed by the fields of that source.

FieldTypeMeaning
sourcestringThe source the record came from, for example amazon.
typestringThe shape of the record. Set by the source; otherwise the event name without a -detail or -full suffix.
eventstringThe name the record was billed under. Always present.
idstring or nullThe identifier the source uses for the item, or null when it has none.
urlstringThe page the record was read from.
collectedAtstringWhen the record was collected, as an ISO 8601 UTC timestamp.
statusstring"not_found" on a record that reports the source's answer that an item you looked up does not exist. Absent on a found record, except on property sources, where it is the listing's own status.
errorobjectPresent only on a record we could not complete, as { stage, reason }. Absent on every other record.
Example envelope
{  "source": "amazon",  "type": "product",  "event": "product-detail",  "id": "B0GG9BY46M",  "url": "https://www.amazon.com/dp/B0GG9BY46M",  "collectedAt": "2026-09-20T20:57:01.663Z"}

Example data. A real Amazon record carries these fields plus the ones listed on the Amazon page.

Why billing follows event, not type

A source can produce records of one type under more than one event, and the events can carry different prices. event is the exact name each record was billed under, so it is the field to reconcile spend against; type tells you the shape of the data, nothing more.

In the price list, Amazon bills product at $4.80 and product-detail at $4.80 per 1,000 records, and a record from either can carry type: "product".

GET /usage reports the month's records per site/event under events, and stats.counts on a job does the same for one run. Multiply each count by its price per 1,000 to check a bill. Pricing and billing has the full list.

Items that do not exist

When you look up one item, for example a product by its id or a property by its URL, and the source answers that it does not exist or was removed, you get a record for it with status: "not_found". It carries the id you asked for (as id and under the source's own id field), the url it was looked up at and nothing else: no other field is filled in. The source answered your question, so the record is billed under its event at the same price as a found one, and it counts towards maxItems. A search or listing that finds nothing returns no record at all.

What a not-found record holds, on every source
FieldOn a not-found record
sourceThe source you asked.
The source's own id fieldThe id you asked for, under the name the source uses for it.
urlThe page the item was looked up at.
status"not_found"
idThe id you asked for.
type and eventThe same as on a found record of that kind. The event sets the price.
collectedAtWhen the source gave its answer.
Every other fieldAbsent.

Records with error

When we cannot complete an item, because the source blocked the request, timed out, or answered in a way we could not read, the record is delivered with an error field and is not charged. Such records do not count towards maxItems either, so a run that asked for 100 can return 100 complete records plus the ones that failed. Requests that returned nothing usable produce no record at all; they appear in stats.failures, one entry per request with a reason. Neither is ever billed: you never pay when we fail to get an answer.

Next step

For runs above 500 records, continue with Jobs: polling, paging, cancelling and how long records are kept.