Skip to content
ScoutingAPI
Docs

Pagination, rate limits & jobs

The cross-cutting mechanics: opaque cursor pagination, per-plan rate-limit headers, the /v1 versioning policy, and the 202 → jobId async pattern for long scrapes.

Cursor pagination

List endpoints (/search and /reviews) use opaque, base64 cursors — never offset or page numbers. Pass the nextCursor from the previous page as cursor; omit it on the first request.

  • limit — page size, default 20, max 100. Out of range → 400 limit_out_of_range.
  • Cursors are opaque — never construct or mutate them. A malformed cursor → 400 invalid_cursor.
  • nextCursor is null and hasMore is false on the last page. Single-object endpoints carry pagination: null.
  • Cursor pagination applies to synchronous results only. When a live list call runs asynchronously, the completed job carries meta.pagination: null — there is no nextCursor to follow. Paginate against the sandbox, live cache hits, or by re-issuing the call; do not expect a cursor to advance across a job result.
Paging through reviews
# Page 1
GET /v1/reviews?platform=booking&listingId=abramovic2&limit=2

# → meta.pagination.nextCursor = "eyJvIjoyLCJsIjoiYWJyYW1vdmljMiJ9"

# Page 2 — pass nextCursor as cursor
GET /v1/reviews?platform=booking&listingId=abramovic2&limit=2&cursor=eyJvIjoyLCJsIjoiYWJyYW1vdmljMiJ9

Rate limits

Two independent token-bucket namespaces: REST is metered per API key; MCP is metered per user. Both are sized by your plan’s rate_limit_per_min and are independent of credits — a request can be rate-limited (429) before any credit logic runs, and 429 is always 0 credits. Read your own limit from code via GET /v1/account (rateLimit.requestsPerMinute).

PlanRequests / min
Free / Sandbox30
Starter60
Pro120
Scale300
EnterpriseCustom

Every response — success and error — advertises the bucket state:

Rate-limit headers
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 117
X-RateLimit-Reset: 1782000060
# On 429 only:
Retry-After: 7

Back off on 429

Async jobs — the 8-second rule

If a live scrape is projected to run longer than ~8 seconds, the request returns 202 with a jobId instead of blocking — in practice that means most live calls run asynchronously. Poll /v1/jobs/{jobId} (free) until data.status is completed, then read the payload from data.result. Cache hits and the sandbox are always served synchronously and never produce a 202. Credits are charged once, on the job’s successful completion — never on the 202 or a poll.

estimatedSeconds is a hint; a job commonly takes tens of seconds and can run several minutes (240s+). Honour the Retry-After the poll returns rather than busy-looping. The SDK auto-polls a 202 to completion — its maxWaitMs defaults to 600s (10 min), enough to wait out a slow scrape; raise it for exceptionally slow searches.

Versioning

  • URI versioning. The major version is the first path segment: /v1 — never a header or query param.
  • Additive changes ship in place under /v1: new endpoints, new optional params, new response fields, new enum values, new error sub-codes. Clients must tolerate unknown fields.
  • Breaking changes go to /v2 and run in parallel. Deprecations get 6 months’ notice, a Sunset header and a changelog entry.