---
title: "Submit a scrape"
method: POST
path: "/api/v1/jobs"
tags: ["MapsData API"]
---

# Submit a scrape

`POST /api/v1/jobs`

## Request

- Base URL: `https://mapsdata.ai` (the document declares no server; this is the origin it was published from)
- URL: `https://mapsdata.ai/api/v1/jobs`
- Auth: `MapsData API key` (a scheme the document does not define)

Start a scrape. Returns immediately with a queued job.

Scrapes take minutes to hours. Poll GET /api/v1/jobs/{id} at a sensible
interval rather than in a tight loop; MapsData also emails you on
completion.

Send an `Idempotency-Key` header to make retries safe: replaying the same
key with the same body returns the original job instead of creating a
second one.

Set `dry_run: true` to validate against every gate, including the quota
check, without creating a job or spending anything.

## Request body

- ScrapeCreate: A new scrape job. `query` must match a canonical Google Maps category. Call GET /api/v1/categories/validate first if you are unsure: an unrecognised query is the single most common reason a submission is rejected, and the error carries suggestions.
  - `query` string, required: One to three comma-separated Google Maps categories, e.g. 'Plumber' or 'Plumber, Electrician'.
  - `country` string: One of us, au, uk, ca.
  - `states` string[]: Full region names for the chosen country, e.g. ['Texas']. Abbreviations are rejected. Omit when passing `cities`.
  - `cities` CityRef[]: Named cities instead of whole regions. Max 50.
    - `city` string, required: City name, e.g. 'Austin'
    - `state` string, required: Full region name, e.g. 'Texas'. Not an abbreviation.
  - `total_limit` integer, required: Rows to scrape. Capped by your plan's per-scrape ceiling; see GET /api/v1/usage.
  - `max_emails_per_company` integer, nullable: Cap emails kept per business. Omit for no cap.
  - `min_reviews` integer: Skip businesses with fewer reviews.
  - `max_reviews` integer, nullable: Skip businesses with more reviews.
  - `list_name` string, nullable: Label for this list. Defaults to the query.
  - `client_id` string, nullable: Only for multi-client workspaces. Defaults to your Main client.
  - `include_variants` boolean: Also search up to four closely related categories, which typically finds materially more unique businesses. Applies only to single-category submissions: with two or three categories your own terms already occupy the fan-out. Set false to search the exact category only.
  - `dry_run` boolean: Validate against every gate and return the resolved plan WITHOUT creating a job or spending any quota.

## Response `202`

Successful Response

- Job
  - `id` string, required
  - `status` string, required: One of: queued, running, cancelling, cancelled, completed, failed.
  - `progress` integer: Percent complete, 0-100.
  - `query` string, nullable
  - `list_name` string, nullable
  - `country` string, nullable
  - `states` string[]
  - `cities` object[]
  - `total_limit` integer, nullable
  - `lead_count` integer: Rows scraped so far. Live during a run.
  - `email_count` integer: Rows carrying an email. Set when the job settles, and the figure your usage is billed on.
  - `stop_reason` string, nullable: Why the scrape stopped. Set on essentially every completed scrape, so compare it against specific values rather than testing whether it is present. One of: 'limit_reached' (hit the requested total), 'coverage_exhausted' (covered every area selected and that was all there was), 'diminishing_returns' (results thinned out, so it stopped rather than walk the long tail), 'barren_query' (the densest areas returned almost nothing), 'user_cancel', 'stall', or 'memory_checkpoint'. The middle three all mean the area genuinely did not hold that many businesses and re-running returns the same count, so a result short of total_limit is expected rather than a failure. NULL means no reason was recorded: the scrape either finished before this field existed, or was cancelled or failed before it was set. Read it with `status`, never on its own.
  - `error_message` string, nullable
  - `export_error` string, nullable
  - `created_at` string, nullable
  - `started_at` string, nullable
  - `completed_at` string, nullable
  - `downloads` DownloadFlags: Which files exist for a job. Booleans, never object keys.
    - `csv` boolean
    - `xlsx` boolean
    - `csv_all` boolean
    - `xlsx_all` boolean
    - `filtered_csv` boolean
    - `filtered_xlsx` boolean

## Other responses

- `400`: Bad Request
- `401`: Unauthorized
- `403`: Forbidden
- `404`: Not Found
- `422`: Validation Error
- `429`: Too Many Requests

---

[API](https://skmtc.dev/mapsdata/apis/mapsdata-api.md) · [All operations](https://skmtc.dev/mapsdata/apis/mapsdata-api/llms.txt) · [OpenAPI document](https://skmtc.dev/mapsdata/apis/mapsdata-api/revisions/c74810530033?raw)
