API
Last updated: September 2, 2026
A scrape is a background job, not a request you wait on. Submitting returns immediately with a job id, and the work happens on our scraping fleet. How long that takes depends far more on how much of the target area we have seen recently than on the size of the job, so plan for a range rather than a number.
queued -> running -> completed
-> failed
-> cancelling -> cancelled| Status | Meaning |
|---|---|
queued | Accepted, waiting for a free worker. |
running | Being scraped. progress and lead_count move. |
completed | Finished. Files are ready to download. |
failed | Something went wrong. error_message says what. Partial results are often still exported and are not billed. |
cancelling | Cancel requested, worker is winding down. |
cancelled | Stopped. Leads already delivered are still billed. |
Minutes to several hours. The dominant factor is how many business websites in the target area we have to visit for the first time to extract an email; areas we have covered recently finish far faster than fresh ones. A job's own row count matters much less than that.
This means you should not build a timeout that assumes a small job is a fast job. Watch the status, not the clock.
Poll at an interval measured in minutes, not seconds. Once a minute is
plenty for a job that will run for an hour, and the rate limit will stop a
tight loop anyway. A reasonable pattern is to start at roughly
max(60, total_limit / 200) seconds and back off from there.
GET /api/v1/jobs/8f2c...
{"id": "8f2c...", "status": "running", "progress": 42,
"lead_count": 210, "email_count": 0,
"stop_reason": null, "downloads": {...}}progress is a percentage, and it is an estimate. It can sit
still while a slow area is worked through.lead_count is rows scraped so far, and moves during the run.email_count is rows carrying a deliverable email. It is set
when the job settles, and it is the figure your allowance is billed on.stop_reason explains an early finish.
diminishing_returns means we stopped because the area was
exhausted and further calls would mostly return businesses you already have.
That is a normal, healthy outcome, not a failure.Newest first, cursor paginated. Pass the previous page's
next_cursor to continue. Do not build page numbers; the cursor is
stable even when new jobs arrive mid-pagination.
GET /api/v1/jobs?limit=25
GET /api/v1/jobs?limit=25&cursor=<next_cursor>
GET /api/v1/jobs?status=runningCheck the downloads object on the job to see which files exist,
then ask for a link. The flags and the download endpoint always agree: if a
flag is false, that file genuinely does not exist and asking for it returns
404 rather than quietly handing you a different one.
GET /api/v1/jobs/8f2c.../download?format=csv&scope=emails
{"url": "https://...", "expires_at": "2026-09-02T22:45:00Z",
"format": "csv", "scope": "emails", "filename": "Plumber.csv"}| Parameter | Values | Meaning |
|---|---|---|
format | csv, xlsx | File type. Defaults to csv. |
scope | emails | Rows with a deliverable email. The default, and what you are billed on. |
all | Every scraped row, email or not. | |
filtered | Output of a filter run, if one exists. |
Links expire after fifteen minutes. Fetch the file promptly, and request a
new link rather than caching the URL. We return the link as JSON rather than
redirecting to it, because a redirect to storage makes some HTTP clients resend
your Authorization header to a third-party host.
POST /api/v1/jobs/8f2c.../cancelA queued scrape stops for free. Cancelling a running scrape still
bills the leads already delivered, the same as in the dashboard, so it
is not a way to undo a submission. A job that has already finished returns
409 job_not_cancellable.
Create a free MapsData account and get 500 Google Maps leads with emails every month. No card needed.
Get 500 free leads a month