/

Results appear as you type. Use the up and down arrow keys to move through them and Enter to open one.

Creating exports over the API, rate limits and pagination

The export job lifecycle over HTTP, how credits are reserved and settled, the per-plan rate limits, and the pagination shape every list endpoint uses.


Creating an export

POST /api/v1/exports
{
  "name": "Accountants — Oslo",
  "query": { …the same body you sent to /search… },
  "format": "csv",          // csv | xlsx | json | parquet
  "columns": ["name", "email", "phone"],
  "select_all_matching": true
}

// -> 202
{ "id": "exp_9f2c…", "status": "queued",
  "estimated_rows": 638, "credits_reserved": 638 }

columns is optional and must be a subset of the 34 canonical group names; omit it for all of them. Note that a group is not a column — eight of them expand to more than one, and a full selection is 64 columns from 34 names. The file is always written in canonical order regardless of the order you list them in.

format must be one your plan includes: csv and xlsx everywhere, json from Growth, parquet from Scale. Asking for one you do not have returns 403 forbidden rather than silently downgrading you.

select_all_matching has to be set deliberately

There is no ambiguity in the API — the field is explicit. The UI equivalent is the trap described in select on page vs select all matching.

Polling it

GET /api/v1/exports/exp_9f2c…
{ "id": "exp_9f2c…", "status": "running",
  "progress": { "done": 412, "total": 638 } }

Statuses are queuedrunningcomplete | incomplete | failed. incomplete is a normal outcome, not an error — partial rows are written and the job can resume. Full explanation in what happens after you press Export.

Poll about once a second for small jobs and back off for large ones.

The rest of the surface

EndpointDoes
GET /api/v1/exportsList: id, name, rows, status, format, credits_used, created_at, downloaded_at
GET /api/v1/exports/:idOne export, with progress
GET /api/v1/exports/:id/download302 to a signed URL, or streams the file. Free, repeatable.
POST /api/v1/exports/:id/resumeRe-queues an incomplete job. Bills only newly written rows.
DELETE /api/v1/exports/:idRemoves the export and its file. Does not refund settled credits.

Credits, precisely

  1. Reserved on enqueue, for estimated_rows.
  2. Settled on completion against rows actually written; the difference is refunded.
  3. Released in full on failed.
  4. Resume bills only what it newly writes.
  5. Re-download costs nothing, ever.

Export creation is idempotent per request, so a retried POST after a timeout cannot double-charge you. See how lead credits work.

Rate limits

PlanRequests per second
StarterNo API access
Growth10
Scale50
Enterprise200

Ten a second sounds tight and is not, because the expensive operation is not the request. A search is one call that returns a count over the entire match set, and an export is one call that runs for minutes on our side. The only thing that genuinely presses against the limit is a tight polling loop, which is why the advice above is to back off rather than to poll faster.

Exceeding it returns 429 with rate_limited. Back off and retry; a plain exponential backoff starting at one second is more than enough. Your current usage is published in the response headers rather than discovered by being cut off, so read it there.

Pagination

Every list endpoint takes ?page=1&per_page=50 (max 200) and answers with the same envelope:

{ "data": [], "page": 1, "per_page": 50, "total": 1234 }

Conventions worth knowing

  • Money is in minor units (cents).
  • Timestamps are ISO-8601 UTC with a trailing Z.
  • Errors always use the { error: { code, message, details } } shape.

Prefer natural language to HTTP? The MCP server exposes the same operations as agent tools.

Updated on:

Was this article helpful?

Related articles