Docs

Call the REST API or connect an AI assistant to find local businesses with owners, emails, and phone numbers.

Quick Start

Four steps take you from a niche to a list of enriched leads. Each example uses the placeholder key lp_example_key; replace it with your own.

1. Create an API key

Press Get API Key, sign in, and create a key on the API keys screen. The REST API is on every plan, including Free. Keep the key private: it spends your credits.

2. Start a search

Send a niche and a location. Replace 1015116 with the location_code that GET /locations?q= returns for your place. A search reserves its depth in credits, charges 1 credit for each business it returns, and refunds the rest in the same response. The response holds a job_id and the raw Google Maps businesses.

bash
curl -X POST https://app.prospectlocally.com/api/v1/search \
  -H "x-api-key: lp_example_key" \
  -H "content-type: application/json" \
  -d '{ "keyword": "plumber", "location_code": 1015116, "depth": 100 }'

3. Poll the job

Enrichment (owner, emails, phones, and website data) continues in the background. Poll the job every 5 seconds until status is completed or failed;enriching means keep waiting. Polling costs nothing.

bash
curl https://app.prospectlocally.com/api/v1/job/JOB_ID \
  -H "x-api-key: lp_example_key"

4. Fetch the results

Read the leads once the job has completed. The format parameter picks JSON or one of the CSV layouts, and reading results costs nothing.

bash
curl "https://app.prospectlocally.com/api/v1/job/JOB_ID/results?format=json" \
  -H "x-api-key: lp_example_key"

MCP Server

The MCP server lets an AI assistant run searches, enrich leads, start campaigns, and export for you. It uses Streamable HTTP at this address:

text
https://app.prospectlocally.com/api/mcp
  • ChatGPT, claude.ai, and GoHighLevel connect by OAuth. Add the URL with no key, sign in, and press Allow.
  • Claude Code, Cursor, and Codex send your API key as a bearer token in the Authorization header.

For Claude Code, one command adds the server:

bash
claude mcp add --transport http prospect-locally https://app.prospectlocally.com/api/mcp \
  --header "Authorization: Bearer lp_example_key"

An assistant sees and spends only the data and credits of the account that signed in or owns the key. Export links last 1 hour; ask the assistant for a new one when it runs out.

Connect your AI

Copy the setup prompt for your tool, paste it into the chat, and ask for leads in plain words.

Instructions for ChatGPT

Add the connector once, sign in, and ask ChatGPT for leads in plain words.

Connect to Prospect Locally so you can find, enrich and export local business leads for me.

Add https://app.prospectlocally.com/api/mcp as a custom connector in ChatGPT. Do not enter an API key. Sign in to Prospect Locally when ChatGPT asks, then press Allow.

Read the REST docs at https://prospectlocally.com/docs and the skill file at https://prospectlocally.com/local-prospects-skill.md.

Work in this order: find_location to resolve the place, search_businesses to start a search, search_status until it finishes, then get_results to read the leads. For many cities, start a campaign and use export_campaign to download the CSV.

Tell me the credit cost before searching, enriching or starting a campaign.

The 19 tools

The server offers these tools. Tools that spend credits say so in their description, so the assistant can quote the cost before it acts.

ToolInputsReturnsCredits
check_creditsnoneplan, credits left, allowance, reset date0
find_locationquery (2+ characters)up to 8 locations0
search_businesseskeyword, location_code, depth (100, 300, 500, 700; default 300), exact_category_keythe POST /search bodyup to depth; unused credits come back
search_statusjob_idthe poll body and credits used0
get_resultsjob_id, has_website, has_email, has_phone, has_mobile_phone, limit (default 50, max 200), pagetrimmed leads0
enrich_domainsleads[] of { domain, business_name, city, state } (1 to 10)the POST /enrich body with each result trimmed1 per domain
find_campaign_locationsquery, country, parent_codecampaign location items0
preview_campaignkeyword, exact_category_key, name, max_leads, include[], exclude[]planned location count, depth, maximum credits0
create_campaignkeyword, exact_category_key, name, max_leads, include[], exclude[], campaign_idthe campaign0
start_campaigncampaign_id (starts or resumes)the campaign1 per business found
pause_campaigncampaign_id, action (pause or stop)the campaign0
list_campaignspage, page_sizecampaigns with status, progress, leads, credits0
get_campaigncampaign_idcampaign detail0
update_campaigncampaign_id, name, max_leads, include[], exclude[]the campaign0
get_campaign_leadscampaign_id, page, page_size (default 50, max 200), searchtrimmed leads0
export_campaigncampaign_id, format (simple, advanced, gohighlevel, instantly, oneclick)export ID and a download link0
get_exportexport_idstatus and a download link valid for 1 hour0
add_search_to_campaigncampaign_id, job_idthe campaign0
verify_phonesnumbers[] (E.164, 1 to 100)the POST /verify body5 per number; free for 30 days after a lookup

Agent Skill

The skill file teaches an AI agent how to use Prospect Locally: the rules to follow before it spends credits, the endpoints, the tools, and the errors. Save it as CLAUDE.md or add it to your agent's instructions. The latest copy is always at /local-prospects-skill.md.

local-prospects-skill.md
# Prospect Locally: agent skill

Save this file as `CLAUDE.md` (or add it to your agent's instructions) so the agent knows how to find, enrich, and export local business leads with Prospect Locally. The latest copy is always at https://prospectlocally.com/local-prospects-skill.md. The full API reference is at https://prospectlocally.com/docs.

## Rules to follow every time

1. Use a campaign when the user wants leads from many areas, such as a state, a country, or a list of cities. A campaign is saved, resumable, and removes duplicates across its locations.
2. Use POST /search for a one-off search, or a search of a few leads in one place, where the user wants the answer in the same conversation.
3. Enrich at most 10 leads in one call to POST /enrich or the enrich_domains tool. Split a longer list into batches of 10.
4. Before you search, enrich, start a campaign, or verify phone numbers, tell the user the credit cost and ask for a yes. Never spend credits on a guess, and never repeat a paid call the user did not ask for.

## What one lead contains

A search for a niche, such as `plumber`, in a place returns the businesses on Google Maps. Every lead is then enriched in the background. One lead holds:

- The Google Maps listing: `name`, `category`, `additional_categories`, `rank`, `gbp_url`, `place_id`, `google_cid`, `website`, and `website_status`.
- Contact data in `contact`: `address`, `city`, `state`, `state_code`, `country`, `lat`, `lng`, `work_hours`, `all_emails[]` (each with `is_role_based` and `is_free_provider`), and `all_phones[]` (each with a number, a `line_type` of mobile, landline, or voip, and a `carrier`).
- The owner: `owner`, `owner_title`, `owner_email`, and `contacts[]` for other named people.
- Reputation in `reputation`: `rating`, `reviews`, `review_snippet`, and `is_claimed`.
- Website data in `web`: `tech` (CMS, analytics, frameworks), `socials` (Facebook, Instagram, LinkedIn, YouTube, Yelp, TikTok), `has_blog`, and `meta_description`.
- Better Business Bureau data in `bbb`: `rating`, `accredited`, and `url`.

Every field is `null` when the data is unknown. Say so to the user instead of guessing a value, and never invent an email address or a phone number.

## Connect

- REST base URL: `https://app.prospectlocally.com/api/v1`. Send the header `x-api-key: lp_example_key` (use the user's own key; keys start with `lp_` and the user creates them on the API keys screen). The REST API is on every plan, including Free.
- MCP server: `https://app.prospectlocally.com/api/mcp` over Streamable HTTP. ChatGPT, claude.ai, and GoHighLevel connect by OAuth: the user adds the URL with no key, signs in, and presses Allow. Claude Code, Cursor, and Codex send `Authorization: Bearer lp_example_key`.
- An agent sees and spends only the credits and data of the account that signed in. Never ask the user to paste a key into a chat when the OAuth sign-in works.
- Try the API with no key and no credits at `https://app.prospectlocally.com/api/v1/demo/*`. Every demo response includes `"demo": true`, and the fixture job ID is `demo-job-plumbers-irvine`.

## Choose the right call

1. Find the `location_code` with `GET https://app.prospectlocally.com/api/v1/locations?q=austin texas` (key required, no credits, up to 8 results).
2. For one place, call `POST https://app.prospectlocally.com/api/v1/search`. For many places, create a campaign.
3. If the niche matches a Google Maps category, look it up with `GET https://app.prospectlocally.com/api/search/categories?q=website designer` (no key, no credits, up to 3 matches). A keyword equal to a category name turns on exact mode, which keeps and charges only businesses whose primary category matches. Pass `exact_category_key: null` to force a broad search.
4. Tell the user the credit cost and wait for a yes before step 2.

## REST endpoints

All paths start with `https://app.prospectlocally.com/api/v1` unless noted.

| Method and path | Inputs | Output |
| --- | --- | --- |
| `GET /locations` | `q` (2+ characters) | up to 8 `{ location_code, name, region, country, type, full_name }` |
| `GET /api/search/categories` | `q` (2 to 80 characters), no key | up to 3 `{ category_key, display_name }` |
| `POST /search` | `keyword`, `location_code`, `depth` (100, 300, 500, or 700; default 300), `exact_category_key` | `job_id` and the raw businesses at once |
| `GET /job/:jobId` | none | `{ job_id, status, keyword, total_businesses, enriched_count, failed_count, progress, created_at, completed_at }` |
| `GET /job/:jobId/results` | `format`, the four filters | leads in the chosen format |
| `POST /enrich` | `leads[]` of `{ domain, business_name?, city?, state? }`, 1 to 10 | `{ total, succeeded, failed, results, usage }` |
| `POST /verify` | `numbers[]` in E.164, 1 to 100 | `{ total, verified, failed, results, usage }` |
| `POST /campaigns` | `keyword`, `exact_category_key`, `campaign_id`, `name`, `max_leads` (minimum 100), `scope` with `include` and `exclude` location codes | the campaign; nothing starts and no credits are spent |
| `POST /campaigns/preview` | the same body | `planned_location_count`, `planned_depth`, `next_search_credits`, maximum credits |
| `GET /campaign-locations` | none, or `q` and `country`, or `parent_code` | location items with `has_children` and `eligible_count` |
| `GET /campaigns`, `GET /campaigns/:id` | `page`, `page_size` | campaigns, or one campaign with its plan and stats |
| `GET /campaigns/:id/plan`, `/searches`, `/leads` | `page`, `page_size` | the ordered locations, the child searches, the deduplicated leads |
| `PATCH /campaigns/:id` | `name`, `max_leads`, `scope` | the campaign (not allowed once completed or stopped) |
| `POST /campaigns/:id/actions/start`, `/pause`, `/resume`, `/stop` | none | the campaign; stop is permanent |
| `POST /campaigns/:id/searches` | `{ "job_id": "..." }` | adds a finished one-off search with the same keyword |
| `GET /campaigns/:id/export` | `format` | CSV of the deduplicated leads |

`POST /search` returns the raw Google Maps rows at once. Enrichment (owner, emails, phones, website data) continues in the background, and you read it from the results endpoint when the job is `completed`.

## MCP tools

The server offers 19 tools. The names below are exact.

| Tool | Inputs | Returns | Credits |
| --- | --- | --- | --- |
| `check_credits` | none | plan, credits left, allowance, reset date | 0 |
| `find_location` | query (2+ characters) | up to 8 locations | 0 |
| `search_businesses` | keyword, location_code, depth (100, 300, 500, 700; default 300), exact_category_key | the POST /search body | up to depth; unused credits come back |
| `search_status` | job_id | the poll body and credits used | 0 |
| `get_results` | job_id, has_website, has_email, has_phone, has_mobile_phone, limit (default 50, max 200), page | trimmed leads | 0 |
| `enrich_domains` | leads[] of { domain, business_name, city, state } (1 to 10) | the POST /enrich body with each result trimmed | 1 per domain |
| `find_campaign_locations` | query, country, parent_code | campaign location items | 0 |
| `preview_campaign` | keyword, exact_category_key, name, max_leads, include[], exclude[] | planned location count, depth, maximum credits | 0 |
| `create_campaign` | keyword, exact_category_key, name, max_leads, include[], exclude[], campaign_id | the campaign | 0 |
| `start_campaign` | campaign_id (starts or resumes) | the campaign | 1 per business found |
| `pause_campaign` | campaign_id, action (pause or stop) | the campaign | 0 |
| `list_campaigns` | page, page_size | campaigns with status, progress, leads, credits | 0 |
| `get_campaign` | campaign_id | campaign detail | 0 |
| `update_campaign` | campaign_id, name, max_leads, include[], exclude[] | the campaign | 0 |
| `get_campaign_leads` | campaign_id, page, page_size (default 50, max 200), search | trimmed leads | 0 |
| `export_campaign` | campaign_id, format (simple, advanced, gohighlevel, instantly, oneclick) | export ID and a download link | 0 |
| `get_export` | export_id | status and a download link valid for 1 hour | 0 |
| `add_search_to_campaign` | campaign_id, job_id | the campaign | 0 |
| `verify_phones` | numbers[] (E.164, 1 to 100) | the POST /verify body | 5 per number; free for 30 days after a lookup |

`get_results` and `get_campaign_leads` return trimmed leads (owner, emails, phones with line type, socials, website status, rating, address) so one call stays small. Ask for the next page instead of raising the limit above 200.

Campaigns created through MCP get a default `max_leads` of 500. Tell the user the cap and raise it only when they ask.

## Poll, then read the results

- Poll `GET /job/:jobId` every 5 seconds. Stop when `status` is `completed` or `failed`; `enriching` means keep waiting. Polling costs nothing.
- Read results with `GET /job/:jobId/results?format=json`. The formats are `json`, `simple`, `advanced`, `ghl`, `instantly`, and `oneclick`. Campaign exports use `advanced`, `simple`, `gohighlevel`, `instantly`, and `oneclick`.
- Four boolean filters narrow the results and combine with AND: `has_website`, `has_email`, `has_phone`, and `has_mobile_phone`. `true` requires the data, `false` requires its absence, and an omitted filter does nothing.
- For a campaign, start it, check `GET /campaigns/:id` now and then, and read `/leads` or export it. A paused campaign resumes after the user adds credits. A campaign in `needs_attention` has a location that failed three retries; show the user and ask before you resume it.
- Export links last 1 hour. Fetch a new one with `get_export` when a link has expired.

## Credits

- One credit pool per account. A search costs 1 credit per business it returns. `POST /enrich` costs 1 per domain. Verify costs 5 per number, and a number verified in the last 30 days is free.
- A search reserves its `depth` before any provider call, keeps only the businesses it returns, and refunds the rest in the same response. The response reports `credits_reserved`, `leads_charged`, `credits_refunded`, and `leads_remaining`. A failed search refunds everything it reserved.
- A search needs 100 available credits to start. The one exception is a single-location search at depth 100, which can run on a balance of 1 to 99.
- A failed `/enrich` domain is refunded and counted in `usage.leads_refunded`. A failed verify lookup is refunded too.
- Polling, reading results, and exporting cost nothing.
- Check the balance with `check_credits` before you quote a cost, and say what the balance will be afterwards.

## Errors

| Status | Meaning | What to do |
| --- | --- | --- |
| 400 | Bad request; the body lists the valid options | Fix the input and try again |
| 401 | The key is missing or invalid | Ask the user for a valid key; do not retry |
| 429 | Not enough credits, or too many requests | See below |
| 500 | Server error | Wait, then retry once; if it fails again, tell the user |

A 429 with `code: "insufficient_credits"` also carries `required_credits` and `available_credits`. Tell the user and stop; they can add credits on the Billing screen. Any other 429 is the rate limit: 60 requests a minute per key, and 120 a minute for job polling. Wait for the number of seconds in the `Retry-After` header.

## Example

```
curl -X POST https://app.prospectlocally.com/api/v1/search \
  -H "x-api-key: lp_example_key" \
  -H "content-type: application/json" \
  -d '{ "keyword": "plumber", "location_code": 1015116, "depth": 100 }'
```

Before you run a call like this one, say: "This search reserves up to 100 credits and charges 1 for each business it returns. Shall I go ahead?"

Authentication

Every REST call sends your API key in the x-api-key header. A key starts with lp_, and you create and revoke keys on the API keys screen of your dashboard. A key reads only the jobs and campaigns of its own account, and every plan, including Free, can use the API. An account holds at most 5 unrevoked API keys by default; revoke one to create another.

bash
curl https://app.prospectlocally.com/api/v1/job/JOB_ID \
  -H "x-api-key: lp_example_key"

If the key is missing or invalid, the API answers 401. Ask for a valid key and do not retry the call. MCP clients that cannot use OAuth send the same key as Authorization: Bearer lp_example_key.

The examples in these docs use the placeholder lp_example_key. Never paste a real key into a chat, a public repository, or a page you share.

Locations

Find the location_code that a search needs. The call matches the text you send against the location list loosely, so miami, denver co, and austin texas all work. It returns up to 8 results, sorted by relevance with US cities first.

GET/api/v1/locations
Authentication
The x-api-key header
Credits
0. Looking up a location is free.

Query parameters

Locations: query parameters
NameTypeRequiredDescription
qstringYesThe place to look for, at least 2 characters.

Example request

bash
curl "https://app.prospectlocally.com/api/v1/locations?q=miami" \
  -H "x-api-key: lp_example_key"

Example response

json
{
  "results": [
    {
      "location_code": 1015116,
      "name": "Miami",
      "region": "Florida",
      "country": "US",
      "type": "City",
      "full_name": "Miami, Florida, US"
    }
  ]
}

Errors

Locations: errors
StatusWhen it happens
400q is missing or shorter than 2 characters.
401The x-api-key header is missing or the key is invalid or revoked. After 20 unknown keys from one address in a minute, the address gets 429 until the minute ends.
429More than 60 requests in a minute on this key. The Retry-After header says how many seconds to wait.
500An error on our side. Retry the call after a short wait.

Categories

Look up the Google Maps category that matches a niche. The call returns up to 3 matches: an exact name first, then the other matches ordered by business count. The path is outside /api/v1 and the call needs no key.

In a search or a campaign, a keyword that equals a category name (ignoring case) turns on exact mode: only businesses whose primary category equals it are kept and charged. Send exact_category_key with no keyword to fill the keyword with the category name, or send exact_category_key: null with a keyword to force a broad search.

GET/api/search/categories
Authentication
None. The call needs no key.
Credits
0. Looking up a category is free.

Query parameters

Categories: query parameters
NameTypeRequiredDescription
qstringYesThe niche to match, 2 to 80 characters.

Example request

bash
curl "https://app.prospectlocally.com/api/search/categories?q=website%20designer"

Example response

json
{
  "categories": [
    { "category_key": "website_designer", "display_name": "Website designer" }
  ]
}

Errors

Categories: errors
StatusWhen it happens
400q is missing, shorter than 2 characters, or longer than 80.
500An error on our side. Retry the call after a short wait.

Campaigns

A campaign is a saved search of one niche over a country, a region, or a list of cities. It searches every location at depth 100, one location at a time, and you can pause it, resume it, and read its leads as they arrive. default_depth and depth_overrides are accepted and ignored.

A campaign is in one of seven statuses: draft, ready, running, paused, needs_attention, completed, or stopped. A failed location is retried after 30 seconds, then after 2.5 minutes, then after 5 minutes; when the third retry fails, the campaign moves to needs_attention. The campaign pauses when credits run out, and resuming it after you add credits continues from every missed location. The campaign stops starting locations once it has charged max_leads unique leads; the cap is not an exact final row count.

POST/api/v1/campaigns
Authentication
The x-api-key header
Credits
Creating, previewing, editing, reading, and exporting cost nothing. A running campaign searches one location at a time. Each location search reserves 100 credits (or the whole balance when fewer than 100 remain) and charges 1 credit for each business it has not charged before in the campaign. Repeated businesses and unused credits are refunded at once.

Body fields

Campaigns: body fields
NameTypeRequiredDescription
keywordstringYes, unless exact_category_key is setThe niche, for example plumber.
exact_category_keystring or nullNoA category_key from the categories lookup. Send null to force a broad search.
campaign_idstring (UUID)NoA UUID you generate. Retrying a create with the same value returns the existing campaign.
namestringNoA label for the campaign.
max_leadsintegerNoThe cap on unique leads charged, at least 100.
scopeobjectYesinclude lists location codes of countries, regions, or cities, in order. exclude lists descendant codes to leave out. A country or region expands into its searchable locations.

Example request

bash
curl -X POST https://app.prospectlocally.com/api/v1/campaigns \
  -H "x-api-key: lp_example_key" \
  -H "content-type: application/json" \
  -d '{
    "keyword": "plumber",
    "name": "Plumbers in Florida",
    "max_leads": 500,
    "scope": { "include": [21142], "exclude": [] }
  }'

Example response

json
{
  "campaign_id": "3f0d6c1e-52a4-4b2a-9d3e-6a1c2f7b8e90",
  "id": "3f0d6c1e-52a4-4b2a-9d3e-6a1c2f7b8e90",
  "name": "Plumbers in Florida",
  "keyword": "plumber",
  "exact_category_key": null,
  "country": "US",
  "include": [21142],
  "exclude": [],
  "max_leads": 500,
  "status": "ready",
  "pause_reason": null,
  "planned_location_count": 412,
  "planned_depth": 100,
  "locations_planned": 412,
  "locations_done": 0,
  "locations_failed": 0,
  "leads_charged": 0,
  "credits_used": 0,
  "duplicates_refunded": 0,
  "last_error": null,
  "created_at": "2026-10-23T14:00:00.000Z",
  "updated_at": "2026-10-23T14:00:00.000Z",
  "started_at": null,
  "finished_at": null
}

Errors

Campaigns: errors
StatusWhen it happens
400A field is invalid, for example max_leads below 100. The answer holds the sentence of the first broken rule and, when a field is at fault, its name in path.
401The x-api-key header is missing or the key is invalid or revoked. After 20 unknown keys from one address in a minute, the address gets 429 until the minute ends.
404The campaign id is unknown, belongs to another account, or is not a UUID (error: Campaign not found). An attach with a job_id that is not one of your searches answers Campaign or search not found.
409code campaign_id_unavailable: a create call sent a campaign_id that another account holds. Send another UUID.
409code campaign_not_editable: a PATCH or an attach on a completed or stopped campaign. The body carries status.
409code invalid_status: an action that does not apply to the campaign's status, for example start on a running campaign. The body carries status, the status the campaign has now.
409code search_not_attachable: an attach of a search that is unfinished (not_finished), has another keyword (keyword_mismatch), or is not a one-off search (not_a_search). The body carries reason.
429A start, or a resume after a credit pause, when the balance cannot pay for the next location. The body is the one Search answers: code: "insufficient_credits", required_credits, and available_credits. Nothing starts and no credit is spent. A key past its rate limit also gets 429.
500An error on our side. Retry the call after a short wait.
500code provider_unavailable: a start or a resume while the Google Maps provider is unavailable. The body carries retry_after_seconds, and the campaign keeps its status.

All campaign calls

Every path below starts with https://app.prospectlocally.com/api/v1. A key reads only the campaigns of its own account.

Campaign calls
MethodPathWhat it does
POST/campaignsCreate a campaign. Nothing starts and no credits are spent.
POST/campaigns/previewPreview a campaign from the same body. Nothing is saved and nothing is charged.
GET/campaign-locationsBrowse locations. With no parameters it lists the country roots; q (with an optional country) searches by name; parent_code lists the children of a region. The answer is { locations }, and each item has location_code, location_name, location_type, country_iso_code, has_children, and eligible_count. A children list stops at 1,000 places and a name search at 50; a list that goes on past its limit also has truncated: true.
GET/campaignsList your campaigns, newest first, 20 to a page (page, page_size up to 100). The answer is { results, total, page, page_size }.
GET/campaigns/:idRead one campaign with its stats, its 20 latest activity entries, the first page of its plan (250 locations), and the first page of its searches (100).
GET/campaigns/:id/planRead the ordered locations, 250 to a page. The answer has the page fields of the list.
GET/campaigns/:id/searchesRead the child searches, 100 to a page. The answer has the page fields of the list.
GET/campaigns/:id/leadsRead the deduplicated leads, 100 to a page. The answer has the page fields of the list and expired_leads. It answers 409 results_restoring while archived searches come back.
PATCH/campaigns/:idChange name, max_leads, or scope while the campaign is draft, ready, running, paused, or needs_attention. The active and finished locations stay as they are. A completed or stopped campaign cannot be edited.
POST/campaigns/:id/actions/startStart a ready campaign.
POST/campaigns/:id/actions/pausePause a running campaign. The location that is running finishes first.
POST/campaigns/:id/actions/resumeResume a paused or needs_attention campaign.
POST/campaigns/:id/actions/stopStop a campaign for good.
POST/campaigns/:id/searchesAttach a finished one-off search with the same keyword. The body is { "job_id": "<uuid>" }. The answer is the campaign with newly_attached, leads_added, and locations_marked.
GET/campaigns/:id/export?format=Export the deduplicated leads as CSV. The format is advanced, simple, gohighlevel, instantly, or oneclick. It answers 409 results_restoring while archived searches come back, and 409 campaign_not_finished while a location is still fetching or enriching.

Preview before you start

The preview takes the body of a create call and answers with the planned location count, the planned depth, the credits the next search needs, the most credits the campaign could use, and your available credits now.

bash
curl -X POST https://app.prospectlocally.com/api/v1/campaigns/preview \
  -H "x-api-key: lp_example_key" \
  -H "content-type: application/json" \
  -d '{ "keyword": "plumber", "scope": { "include": [21142] } }'
json
{
  "planned_location_count": 412,
  "planned_depth": 100,
  "next_search_credits": 100,
  "max_credits": 41200,
  "available_credits": 1000
}

Start, pause, resume, and stop

Each action answers with the campaign after the action. A start, and a resume after a credit pause, first check that the balance can pay for the next location; when it cannot, the call answers 429 and nothing starts.

bash
curl -X POST https://app.prospectlocally.com/api/v1/campaigns/3f0d6c1e-52a4-4b2a-9d3e-6a1c2f7b8e90/actions/start \
  -H "x-api-key: lp_example_key"
json
{
  "campaign_id": "3f0d6c1e-52a4-4b2a-9d3e-6a1c2f7b8e90",
  "id": "3f0d6c1e-52a4-4b2a-9d3e-6a1c2f7b8e90",
  "name": "Plumbers in Florida",
  "keyword": "plumber",
  "exact_category_key": null,
  "country": "US",
  "include": [21142],
  "exclude": [],
  "max_leads": 500,
  "status": "running",
  "pause_reason": null,
  "planned_location_count": 412,
  "planned_depth": 100,
  "locations_planned": 412,
  "locations_done": 0,
  "locations_failed": 0,
  "leads_charged": 0,
  "credits_used": 0,
  "duplicates_refunded": 0,
  "last_error": null,
  "created_at": "2026-10-23T14:00:00.000Z",
  "updated_at": "2026-10-23T14:09:30.000Z",
  "started_at": "2026-10-23T14:05:00.000Z",
  "finished_at": null
}

List campaigns

The list answers with one page of summaries, newest first. total counts all your campaigns, not only this page. The plan, searches, and leads lists answer with the same four page fields.

bash
curl "https://app.prospectlocally.com/api/v1/campaigns?page=1&page_size=20" \
  -H "x-api-key: lp_example_key"
json
{
  "results": [
    {
      "campaign_id": "3f0d6c1e-52a4-4b2a-9d3e-6a1c2f7b8e90",
      "id": "3f0d6c1e-52a4-4b2a-9d3e-6a1c2f7b8e90",
      "name": "Plumbers in Florida",
      "keyword": "plumber",
      "status": "running",
      "locations_done": 3,
      "locations_planned": 412,
      "leads_charged": 118,
      "credits_used": 118,
      "created_at": "2026-10-23T14:00:00.000Z"
    }
  ],
  "total": 1,
  "page": 1,
  "page_size": 20
}

Read one campaign

The campaign answers with all the fields of the create answer, plus stats, events (newest first), and the first pages of plan and searches. Read the other pages from /campaigns/:id/plan and /campaigns/:id/searches. archive counts the searches behind the leads that moved to the archive 14 days after they finished. Reading the leads list or the export brings them back: while they come back, both answer 409 with code: "results_restoring" and Retry-After: 60. The leads list reports expired_leads, the leads of searches older than 12 months, which are gone.

bash
curl https://app.prospectlocally.com/api/v1/campaigns/3f0d6c1e-52a4-4b2a-9d3e-6a1c2f7b8e90 \
  -H "x-api-key: lp_example_key"
json
{
  "campaign_id": "3f0d6c1e-52a4-4b2a-9d3e-6a1c2f7b8e90",
  "id": "3f0d6c1e-52a4-4b2a-9d3e-6a1c2f7b8e90",
  "name": "Plumbers in Florida",
  "keyword": "plumber",
  "exact_category_key": null,
  "country": "US",
  "include": [21142],
  "exclude": [],
  "max_leads": 500,
  "status": "running",
  "pause_reason": null,
  "planned_location_count": 412,
  "planned_depth": 100,
  "locations_planned": 412,
  "locations_done": 3,
  "locations_failed": 0,
  "leads_charged": 118,
  "credits_used": 118,
  "duplicates_refunded": 14,
  "last_error": null,
  "created_at": "2026-10-23T14:00:00.000Z",
  "updated_at": "2026-10-23T14:09:30.000Z",
  "started_at": "2026-10-23T14:05:00.000Z",
  "finished_at": null,
  "stats": {
    "locations_done": 3,
    "locations_failed": 0,
    "locations_planned": 412,
    "leads_charged": 118,
    "leads_in_pool": 118,
    "credits_used": 118,
    "duplicates_refunded": 14
  },
  "archive": {
    "archived": 0,
    "restoring": 0,
    "expired": 0
  },
  "events": [
    {
      "id": "7c1e2a90-3b4d-4f56-8a7b-9c0d1e2f3a4b",
      "kind": "location_done",
      "message": "Miami finished with 62 leads.",
      "created_at": "2026-10-23T14:09:30.000Z"
    },
    {
      "id": "5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d",
      "kind": "started",
      "message": "The campaign started.",
      "created_at": "2026-10-23T14:05:00.000Z"
    }
  ],
  "plan": {
    "results": [
      {
        "n": 1,
        "location_code": 1015116,
        "location_name": "Miami",
        "location_type": "City",
        "status": "done",
        "attempt": 1,
        "next_attempt_at": null,
        "search_id": "b1d9c6f2-7a34-4e58-9c1b-2f6d8e0a4c37",
        "attached": false,
        "error_code": null,
        "error_message": null
      }
    ],
    "total": 412,
    "page": 1,
    "page_size": 250
  },
  "searches": {
    "results": [
      {
        "id": "b1d9c6f2-7a34-4e58-9c1b-2f6d8e0a4c37",
        "kind": "campaign_location",
        "attached": false,
        "keyword": "plumber",
        "status": "completed",
        "location_names": ["Miami"],
        "leads_charged": 62,
        "credits_used": 62,
        "created_at": "2026-10-23T14:05:01.000Z",
        "finished_at": "2026-10-23T14:06:12.000Z"
      }
    ],
    "total": 3,
    "page": 1,
    "page_size": 100
  }
}

Browse locations

Use the location codes in scope.include and scope.exclude. eligible_count is the number of searchable locations under the item, and has_children says whether parent_code lists more.

bash
curl "https://app.prospectlocally.com/api/v1/campaign-locations?q=florida&country=US" \
  -H "x-api-key: lp_example_key"
json
{
  "locations": [
    {
      "location_code": 21142,
      "location_name": "Florida",
      "location_type": "State",
      "country_iso_code": "US",
      "has_children": true,
      "eligible_count": 412
    }
  ]
}

Attach a finished search

Attach adds a finished one-off search with the campaign's keyword to the campaign's leads, and charges and refunds nothing. The answer is the campaign with three more fields: newly_attached (false when the search was attached before), leads_added, and locations_marked.

bash
curl -X POST https://app.prospectlocally.com/api/v1/campaigns/3f0d6c1e-52a4-4b2a-9d3e-6a1c2f7b8e90/searches \
  -H "x-api-key: lp_example_key" \
  -H "content-type: application/json" \
  -d '{ "job_id": "b1d9c6f2-7a34-4e58-9c1b-2f6d8e0a4c37" }'
json
{
  "campaign_id": "3f0d6c1e-52a4-4b2a-9d3e-6a1c2f7b8e90",
  "id": "3f0d6c1e-52a4-4b2a-9d3e-6a1c2f7b8e90",
  "name": "Plumbers in Florida",
  "keyword": "plumber",
  "exact_category_key": null,
  "country": "US",
  "include": [21142],
  "exclude": [],
  "max_leads": 500,
  "status": "running",
  "pause_reason": null,
  "planned_location_count": 412,
  "planned_depth": 100,
  "locations_planned": 412,
  "locations_done": 3,
  "locations_failed": 0,
  "leads_charged": 118,
  "credits_used": 118,
  "duplicates_refunded": 14,
  "last_error": null,
  "created_at": "2026-10-23T14:00:00.000Z",
  "updated_at": "2026-10-23T14:09:30.000Z",
  "started_at": "2026-10-23T14:05:00.000Z",
  "finished_at": null,
  "newly_attached": true,
  "leads_added": 62,
  "locations_marked": 1
}

Poll Status

Read the progress of a search job. Poll every 5 seconds until status is completed or failed; enriching means keep waiting. This call allows 120 requests a minute per key, twice the limit of the other endpoints.

GET/api/v1/job/:jobId
Authentication
The x-api-key header
Credits
0. Polling is free.

Path parameter

Poll Status: path parameter
NameTypeRequiredDescription
jobIdstringYesThe job_id that the search returned.

Example request

bash
curl https://app.prospectlocally.com/api/v1/job/b1d9c6f2-7a34-4e58-9c1b-2f6d8e0a4c37 \
  -H "x-api-key: lp_example_key"

Example response

json
{
  "job_id": "b1d9c6f2-7a34-4e58-9c1b-2f6d8e0a4c37",
  "status": "enriching",
  "keyword": "plumber",
  "total_businesses": 62,
  "enriched_count": 41,
  "failed_count": 1,
  "progress": 68,
  "created_at": "2026-10-23T14:02:11Z",
  "completed_at": null
}

Errors

Poll Status: errors
StatusWhen it happens
401The x-api-key header is missing or the key is invalid or revoked. After 20 unknown keys from one address in a minute, the address gets 429 until the minute ends.
429More than 120 requests in a minute on this key. Wait for the Retry-After seconds.
500An error on our side. Retry the call after a short wait.

Get Results

Read the enriched leads of a job. The format parameter picks JSON or one of five CSV layouts: simple, advanced, ghl (GoHighLevel), instantly (rows without an email are dropped), and oneclick. A CSV answer uses the text/csv type with headers for the attachment, the result format, and the result count. The Advanced CSV has the same columns in the same order as the Advanced export in the app.

Four filters narrow the results and combine with AND. true requires the data, false requires its absence, and an omitted filter does nothing.

GET/api/v1/job/:jobId/results
Authentication
The x-api-key header
Credits
0. Reading results is free.

Query parameters

Get Results: query parameters
NameTypeRequiredDescription
formatstringNojson, simple, advanced, ghl, instantly, or oneclick.
has_websitebooleanNoThe business has a website URL.
has_emailbooleanNoAt least one email was found.
has_phonebooleanNoAt least one phone of any type was found.
has_mobile_phonebooleanNoAt least one phone is labelled mobile.

Example request

bash
curl "https://app.prospectlocally.com/api/v1/job/b1d9c6f2-7a34-4e58-9c1b-2f6d8e0a4c37/results?format=json&has_email=true" \
  -H "x-api-key: lp_example_key"

Example response

json
{
  "job_id": "b1d9c6f2-7a34-4e58-9c1b-2f6d8e0a4c37",
  "total": 1,
  "results": [
    {
      "id": "0b8e5d3c-6f21-4a97-8d14-5c2e7a9b1f60",
      "job_id": "b1d9c6f2-7a34-4e58-9c1b-2f6d8e0a4c37",
      "name": "Harbor Light Plumbing",
      "category": "Plumber",
      "website": "https://harborlightplumbing.example",
      "owner": "Dana Whitfield",
      "owner_title": "Owner",
      "email": "[email protected]",
      "phone": "+13055550142",
      "phone_type": "landline",
      "mobile_number": "+13055550177",
      "contact": {
        "city": "Miami",
        "state_code": "FL",
        "all_emails": [
          { "email": "[email protected]", "is_role_based": false, "is_free_provider": false }
        ],
        "all_phones": [
          { "number": "+13055550142", "line_type": "landline", "carrier": "Example Telecom", "verified": null }
        ]
      },
      "web": { "socials": { "facebook": "https://facebook.example/harborlightplumbing" } },
      "bbb": { "rating": "A+", "accredited": true }
    }
  ]
}

Errors

Get Results: errors
StatusWhen it happens
400The format or a filter has a value the API does not support. The answer lists the valid options.
401The x-api-key header is missing or the key is invalid or revoked. After 20 unknown keys from one address in a minute, the address gets 429 until the minute ends.
404The job id is unknown, belongs to another account, is not a UUID, or names an enrich request or a campaign location rather than a one-off search (error: Job not found).
409code: "results_restoring": the search was archived and is being restored; call again after the Retry-After of 60 seconds. code: "job_not_finished": a CSV format was asked for while the job is still enriching; retry when the job status is completed. JSON is served at any time.
410code results_expired: the search is older than the 12-month archive, so its results are gone.
429More than 60 requests in a minute on this key. The Retry-After header says how many seconds to wait.
429The key already has 2 downloads open (code: "too_many_downloads"). Finish one and retry after the Retry-After seconds. A download that is still open after 2 minutes is ended, so read the body as it arrives.
500An error on our side. Retry the call after a short wait.

Enrich

Enrich 1 to 10 websites that you already know, without a Google Maps search. The call runs synchronously and answers with the owner, emails, phones, and website data of each domain. Each result has the lead shape without the fields that only a Maps search fills: no reputation, rank, or main_image.

POST/api/v1/enrich
Authentication
The x-api-key header
Credits
1 credit for each domain. The whole batch is charged up front in one deduction. A failed domain is refunded and counted in usage.leads_refunded.

Body fields

Enrich: body fields
NameTypeRequiredDescription
leadsarrayYes1 to 10 items. A null body or a null leads value returns 400.
leads[].domainstringYesA bare hostname such as example.com. IP addresses, IPv6 addresses, reserved hostnames, and protocol prefixes return 400.
leads[].business_namestringNoHelps match the owner.
leads[].citystringNoHelps match the owner.
leads[].statestringNoHelps match the owner.
Idempotency-Key (header)stringNo1 to 255 visible ASCII characters that you choose for this request. A retry with the same key within 24 hours answers with the first request's result and charges nothing again. Send one whenever you might retry after a timeout.

Example request

bash
curl -X POST https://app.prospectlocally.com/api/v1/enrich \
  -H "x-api-key: lp_example_key" \
  -H "content-type: application/json" \
  -d '{ "leads": [{ "domain": "harborlightplumbing.com", "business_name": "Harbor Light Plumbing" }] }'

Example response

json
{
  "total": 1,
  "succeeded": 1,
  "failed": 0,
  "results": [
    {
      "domain": "harborlightplumbing.com",
      "status": "succeeded",
      "result": {
        "name": "Harbor Light Plumbing",
        "owner": "Dana Whitfield",
        "email": "[email protected]",
        "phone_type": "landline"
      },
      "error": null
    }
  ],
  "usage": { "leads_charged": 1, "leads_refunded": 0, "leads_remaining": 9937 }
}

Errors

Enrich: errors
StatusWhen it happens
400A domain is not a bare hostname, the body is null, or leads is null or not 1 to 10 items.
401The x-api-key header is missing or the key is invalid or revoked. After 20 unknown keys from one address in a minute, the address gets 429 until the minute ends.
422The Idempotency-Key was used with a different body in the last 24 hours (code: "idempotency_key_reused"). Nothing is charged.
429The balance cannot cover the whole batch (code: "insufficient_credits"), the account already has 50 domains being enriched (code: "too_many_enrich", with open and limit), or the rate limit applies. No partial charge is made.
500An error on our side. Retry the call after a short wait.

Verify

Look up the live line type and carrier of 1 to 100 phone numbers. The result replaces the free label everywhere the number appears. Verify runs on paid plans only; a Free account gets a 403 with code: "paid_plan_required" before any lookup runs.

POST/api/v1/verify
Authentication
The x-api-key header
Credits
5 credits for each number looked up, reserved up front in one deduction. A number verified in the last 30 days returns cached and is free. A lookup that fails on a transport error, a 422, or an answer with no carrier data is refunded.

Body fields

Verify: body fields
NameTypeRequiredDescription
numbersarray of stringsYes1 to 100 phone numbers in E.164 format, for example +13055550142.
Idempotency-Key (header)stringNo1 to 255 visible ASCII characters that you choose for this request. A retry with the same key within 24 hours answers with the first request's result and charges nothing again. Send one whenever you might retry after a timeout.

Example request

bash
curl -X POST https://app.prospectlocally.com/api/v1/verify \
  -H "x-api-key: lp_example_key" \
  -H "content-type: application/json" \
  -d '{ "numbers": ["+13055550142", "+13055550177"] }'

Example response

json
{
  "total": 2,
  "verified": 1,
  "failed": 0,
  "results": [
    {
      "number": "+13055550142",
      "status": "verified",
      "line_type": "landline",
      "carrier": "Example Telecom",
      "verified_at": "2026-10-23T14:05:40Z",
      "error": null
    },
    {
      "number": "+13055550177",
      "status": "cached",
      "line_type": "mobile",
      "carrier": "Example Mobile",
      "verified_at": "2026-10-12T09:30:02Z",
      "error": null
    }
  ],
  "usage": { "credits_charged": 5, "credits_refunded": 0, "leads_remaining": 9932 }
}

Errors

Verify: errors
StatusWhen it happens
400numbers is missing, has more than 100 entries, or holds a value that is not E.164.
401The x-api-key header is missing or the key is invalid or revoked. After 20 unknown keys from one address in a minute, the address gets 429 until the minute ends.
403The account is on the Free plan: code: "paid_plan_required" and the error "Verify needs a paid plan." No lookup runs and no credit is spent.
409The request with this Idempotency-Key is still looking numbers up (code: "request_in_progress"). Retry after the Retry-After seconds; nothing is charged again.
422The Idempotency-Key was used with a different body in the last 24 hours (code: "idempotency_key_reused"). Nothing is charged.
429The balance cannot cover the whole request (code: "insufficient_credits"). No lookup runs. The rate limit also answers 429.
500An error on our side. Retry the call after a short wait.

Demo Endpoints

Try the API with no key and no credits. The demo endpoints answer from fixed fixtures, never call a provider, and never touch your balance. Every response includes "demo": true.

The fixture job is demo-job-plumbers-irvine. Its businesses are fictional, with websites on reserved .example domains. Every path below starts with https://app.prospectlocally.com/api/v1.

The demo answers are fixed, so a few inputs behave differently from the live API. The location codes 999000001 to 999000003 are fictional: only the demo endpoints know them, and the live Search refuses them. Only the domains of the fixture businesses enrich, for example exampleplumbing.example; any other valid domain answers failed with enrichment_failed. Only +19495550101, +19495550102, and +19495550103 verify; every other number fails with demo_number.

Demo endpoints
MethodPathWhat it does
GET/demo/locations?q=Look up locations, as GET /locations does.
POST/demo/searchA search that returns the fixture job.
GET/demo/job/:jobIdThe job status. Add ?status=enriching to see a job that is still running.
GET/demo/job/:jobId/resultsThe fixture leads in every format and with every filter.
POST/demo/enrichAn enrichment of fixture domains. /demo/enhance is an alias.
POST/demo/verifyA verification of fixture numbers.

Example request

bash
curl https://app.prospectlocally.com/api/v1/demo/job/demo-job-plumbers-irvine/results?format=json

Example response

json
{
  "demo": true,
  "job_id": "demo-job-plumbers-irvine",
  "total": 10,
  "results": [
    {
      "id": "00000000-0000-4000-8000-000000000001",
      "job_id": "demo-job-plumbers-irvine",
      "name": "Example Plumbing Co",
      "category": "Plumber",
      "website": "https://exampleplumbing.example",
      "owner": "Sam Example",
      "owner_title": "Owner",
      "email": "[email protected]",
      "phone": "+19495550102",
      "phone_type": "landline",
      "mobile_number": "+19495550101",
      "contact": {
        "city": "Irvine",
        "state_code": "CA",
        "all_emails": [
          { "email": "[email protected]", "is_role_based": false, "is_free_provider": false }
        ],
        "all_phones": [
          { "number": "+19495550102", "line_type": "landline", "carrier": "Example Telecom", "verified": null }
        ]
      },
      "web": { "socials": { "facebook": "https://facebook.example/exampleplumbing" } },
      "bbb": { "rating": "A+", "accredited": true }
    }
  ]
}

Data Fields

A lead has more than 50 distinct fields. Every field is null when the data is unknown, so check for null before you use a value, and never guess one. The tables below follow the JSON format; the CSV formats use the column names of the export you pick. summary is always null and services is always empty.

Top level

Top-level lead fields
FieldTypeDescription
idstringThe lead id.
job_idstringThe search job that found the lead.
namestringThe business name.
categorystringThe primary Google Maps category.
additional_categories[]string[]The other Google Maps categories of the business.
websitestringThe business website. Null when the listing has none or links to a social or directory page.
website_statusstringThe result of the website check, for example ok, no_website, dead, or blocked.
gbp_urlstringThe Google Maps profile URL.
google_cidstringThe Google CID. A string of digits; the alias cid holds the same value.
place_idstringThe Google Place ID.
logostringThe logo URL read from the website.
main_imagestringThe main image of the Google Maps listing.
rankintegerThe position in the Google Maps results.
ownerstringThe owner's full name, read from the website and the BBB profile.
owner_first_namestringThe owner's first name, with honorifics removed.
owner_last_namestringThe owner's last name, with suffixes removed.
owner_titlestringThe owner's title, for example Owner, Founder, or President.
owner_verifiedbooleanTrue when the website and the BBB profile name the same person.
owner_emailstringAn email whose local part matches the owner's name. Null when none matches.
contact_2_namestringThe second person named, flattened from contacts[].
contact_2_titlestringThe second person's title.
contact_3_namestringThe third person named, flattened from contacts[].
contact_3_titlestringThe third person's title.
email_contact_namestringThe first name read from a personal-looking email address. Never read from a role word such as info.
summarynullAlways null. The API does not write summaries.
emailstringThe best-match email: the owner's, then a named address on the business domain, then a role address, then a free-provider address.
phonestringThe Google profile number, or the best discovered number when the profile has none.
phone_typestringThe line type of phone: mobile, landline, voip, or toll_free.
mobile_numberstringThe first mobile number found.
created_atstringWhen the lead was created, as an ISO 8601 timestamp.

contacts[]

Everyone named on the website or the BBB profile, owner first, then by title strength, with duplicates merged.

contacts[] fields
FieldTypeDescription
contacts[].namestringThe person's name.
contacts[].titlestringThe person's title.
contacts[].sources[]string[]Where the name was found.
contacts[].emailstringThe person's email, when one matches.

bbb

Better Business Bureau data for US and Canadian businesses. The whole object is null when nothing matches.

bbb fields
FieldTypeDescription
bbb.urlstringThe BBB profile URL.
bbb.ratingstringThe BBB rating, A+ to F.
bbb.accreditedbooleanTrue when the business is BBB accredited.
bbb.business_startedstringWhen the business started.
bbb.entity_typestringThe entity type, for example a corporation.

contact

Where the business is and how to reach it. A phone number carries verified once you verify it; a number that was never verified has verified set to null.

contact fields
FieldTypeDescription
contact.addressstringThe street address.
contact.citystringThe city.
contact.statestringThe state or region name.
contact.state_codestringThe state code, derived for the US and Canada.
contact.countrystringThe country.
contact.latnumberThe latitude.
contact.lngnumberThe longitude.
contact.work_hoursobjectThe opening hours for each day.
contact.contact_urlstringThe contact page URL.
contact.all_emails[]object[]Every email found, with the three fields below.
contact.all_emails[].emailstringThe address.
contact.all_emails[].is_role_basedbooleanTrue for addresses such as info or sales.
contact.all_emails[].is_free_providerbooleanTrue for addresses at a free provider such as Gmail.
contact.all_phones[]object[]Every phone found, the Google number first, deduplicated by digits.
contact.all_phones[].numberstringThe number. International numbers keep their format.
contact.all_phones[].line_typestringmobile, landline, voip, or toll_free.
contact.all_phones[].carrierstringThe carrier name.
contact.all_phones[].verifiedobjectSet after Verify: at, line_type, and carrier. Null before.

reputation

reputation fields
FieldTypeDescription
reputation.ratingnumberThe Google rating.
reputation.reviewsintegerThe number of Google reviews.
reputation.rating_distributionobjectThe count of reviews for each rating from 1 to 5.
reputation.review_snippetstringA sample review.
reputation.is_claimedbooleanTrue when the owner claimed the Google listing.

services

Always empty. The object is present so a client can rely on its shape; lists are [] and the other fields are null.

services fields
FieldTypeDescription
services.list[]string[]Always [].
services.specialties[]string[]Always [].
services.areas[]string[]Always [].
services.selling_points[]string[]Always [].
services.customer_typesnullAlways null.
services.year_establishednullAlways null.
services.employeesnullAlways null.

web

What the crawler read from the business website.

web fields
FieldTypeDescription
web.techobjectThe technology found: cms, cdn, server, chat, analytics[], frameworks[], advertising[], and other[].
web.socialsobjectProfile URLs: facebook, instagram, linkedin, youtube, yelp, and tiktok. Null for a profile that was not found.
web.has_blogbooleanTrue when the site has a blog or news section.
web.meta_descriptionstringThe meta description of the home page.
web.pages_crawledintegerHow many pages the crawler read.
web.website_contentstringThe first 1,000 characters of visible text.
web.pages[]object[]Each page read, with url, title, and meta_description.
web.theme_colorstringThe theme color of the site.
web.schema_org[]object[]The schema.org data found on the pages.

searched_location

searched_location fields
FieldTypeDescription
searched_location.citystringThe location the search asked about.
searched_location.location_codeintegerThe location code the search used.

Raw search rows

The immediate answer of POST /search carries the Google Maps fields only. Enrichment adds the lead fields above while the job runs.

Raw search row fields
FieldTypeDescription
namestringThe business name.
phonestringThe Google profile number.
websitestringThe website URL.
addressstringThe full address.
citystringThe city.
statestringThe state or region.
country_iso_codestringThe two-letter country code.
ratingnumberThe Google rating.
reviewsintegerThe number of reviews.
rating_distributionobjectThe reviews for each rating from 1 to 5.
cidstringThe Google CID.
google_cidstringThe same value as cid.
place_idstringThe Google Place ID.
categorystringThe primary category.
additional_categoriesstring[]The other categories.
latitudenumberThe latitude.
longitudenumberThe longitude.
is_claimedbooleanTrue when the owner claimed the listing.
rank_groupintegerThe position among the map results.
rank_absoluteintegerThe position among all results.
main_imagestringThe main image URL.
total_photosintegerThe number of photos.
snippetstringThe snippet Google shows.
review_snippetstringA sample review.
price_levelstringinexpensive, moderate, expensive, very_expensive, or null.
book_online_urlstringThe booking URL.
work_hoursobjectThe opening hours for each day.
searched_citystringThe location the search asked about.

Plans

Every plan, including Free, includes API access, the full enrichment pipeline, and all five CSV formats. The plans differ in price and credits only.

Plans, prices, and credits
PlanMonthly priceYearly priceCreditsAPI access
Free$0Not offered100 credits once, for the life of the accountYes
Starter$39$29 a month, billed $348 a year10,000 credits every monthYes
Agency$97$79 a month, billed $948 a year50,000 credits every monthYes
Scale$297$229 a month, billed $2,748 a year200,000 credits every monthYes
  • A search costs 1 credit for each business it returns, and Enrich costs 1 credit for each domain. Verify costs 5 credits for each number, and a number verified in the last 30 days is free.
  • Polling a job, reading results, and exporting cost nothing.
  • Paid credits renew every month on the anniversary of the subscription, including on a yearly plan, and they do not roll over. Free credits never reset.
  • Credits are spent in this order: plan credits, then free credits, then add-on credits. A paid subscriber can buy an add-on pack of 10,000 credits for $39; add-on credits never reset or expire.

Errors

A failed call answers with an HTTP status and a JSON body that holds an error message. Seven statuses cover every failure, and 429 has two causes.

400: bad request

The body or a parameter is invalid. The answer lists the valid options, for example the four depth values or the supported result formats. Fix the input and send the call again.

json
{
  "error": "depth must be one of the valid values",
  "valid_options": [100, 300, 500, 700]
}

401: missing or invalid key

The x-api-key header is missing, or the key is invalid or revoked. Do not retry the call; create a valid key first.

json
{
  "error": "Missing or invalid API key"
}

404: not found

The job or campaign id is unknown, belongs to another account, or is not a UUID. The body names what was not found, for example Job not found.

409: conflict

The call conflicts with the current state. Get Results answers 409 with code: "results_restoring" while an archived search is restored, and a Retry-After header of 60 seconds says when to call again. The campaign calls answer 409 for an action that does not fit the campaign's status; each code is listed on its endpoint.

410: results expired

The search is older than the 12-month archive, so Get Results has nothing to return. The body carries code: "results_expired". Do not retry the call.

429: not enough credits

The balance cannot cover the call, and the API refuses it before any provider call, so no credit is spent. The body carries code: "insufficient_credits", the credits the call needs in required_credits, and the credits you have in available_credits. A search needs at least 100 available credits, except a depth-100 search on a final balance of 1 to 99. Add credits, or send a smaller call.

json
{
  "error": "Not enough lead credits -- 300 required to search",
  "code": "insufficient_credits",
  "required_credits": 300,
  "available_credits": 100
}

429: rate limit

A key can send 60 requests a minute on every endpoint except job polling, which allows 120. Past the limit the API answers 429 with a Retry-After header that holds the seconds to wait. Both limits count each key separately.

http
HTTP/1.1 429 Too Many Requests
Retry-After: 12

{
  "error": "Rate limit exceeded",
  "code": "rate_limited"
}

500: server error

An error on our side. A failed search refunds its whole reservation. Retry the call after a short wait.

json
{
  "error": "Something went wrong on our side"
}