Business Monster API
Everything the dashboard does, from your own code. Start a Google Maps search, poll it, and download every business with contact details, verified emails, social profiles, reviews and photos as JSON, NDJSON, CSV, XLSX or XML.
https://api.businessmonster.co/v1Auth: Bearer API key1 credit = 1 businessNo minimum per searchWorks with Claude & ChatGPT (MCP)API keys are created in your dashboard under API. New accounts start with 20 free credits, so you can make your first calls before buying anything.
Quick start
Three calls: start a search, wait for it to finish, download the records.
# 1. Start a search
curl -X POST https://api.businessmonster.co/v1/searches \
-H "Authorization: Bearer bm_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"location": "Austin, TX, USA", "keyword": "dentist", "max_results": 500}'
# 2. Poll until "status" is "completed" (every 5 to 10 seconds is fine)
curl https://api.businessmonster.co/v1/searches/SEARCH_ID \
-H "Authorization: Bearer bm_live_YOUR_KEY"
# 3. Download the records in the format you need
curl "https://api.businessmonster.co/v1/searches/SEARCH_ID/results?format=xlsx" \
-H "Authorization: Bearer bm_live_YOUR_KEY" -o dentists-austin.xlsx
Authentication
Send your key in the Authorization header of every request:
Authorization: Bearer bm_live_23e8a631…
- Keys are created and revoked in the dashboard (API page). A key is shown once, at creation; we store only a hash of it.
- You can have up to 5 active keys, for example one per integration. Revoking a key stops it immediately.
- Every key acts on the credits of the account that created it. Keep keys on your server, never in browser code.
AI assistants (MCP)
Business Monster ships an MCP server, so Claude, ChatGPT, Cursor and any other assistant that speaks the Model Context Protocol can run searches for you directly in the conversation. Ask for "opticians in Kütahya with phone numbers" and the assistant starts the search, waits for it, and hands you the file. It spends the credits on your own account, at the same prices.
https://api.businessmonster.co/mcpStreamable HTTPStatelessProtocol 2024-11-05 to 2026-07-28Connecting
If your client can send headers (Cursor, Claude Code, n8n, most SDKs), point it at the server URL and add your key as a Bearer token:
{
"mcpServers": {
"business-monster": {
"url": "https://api.businessmonster.co/mcp",
"headers": { "Authorization": "Bearer bm_live_YOUR_KEY" }
}
}
}
If your client only accepts a URL (for example Claude's "Add custom connector" box), put the key in the path instead. It works exactly the same:
https://api.businessmonster.co/mcp/k/bm_live_YOUR_KEY
Tools the assistant gets
| Tool | What it does |
|---|---|
| search_businesses | Starts a search from a location and a keyword. Returns a search id straight away. Spends credits. |
| get_search | Status and progress. Takes wait_seconds (up to 45) so the assistant waits on the server instead of polling in a loop. |
| get_search_results | A page of compact records (name, category, address, phone, website, email, rating, socials). Ask for specific fields, a page size, or the last N. |
| cancel_search | Cancels a search that has not started yet and releases its reserved credits. |
| list_searches | Your recent searches with status and credit cost. |
| get_account | Credit balance, current prices and limits. |
| list_available_fields | The fields a business record can contain, grouped by topic, so the assistant can answer "can you get their Instagram?" without guessing. |
What to expect
- Results come back a page at a time. A conversation cannot hold a thousand rows, so the assistant reads 25 records at a time, up to 200 if it asks, and can request only the fields you asked for. The complete file with all 100+ fields is downloaded from your dashboard, never handed out as a link in the chat.
- Searches are not instant. A few hundred businesses take one to five minutes. The assistant checks the status every 15 to 30 seconds while it waits.
- Same credits, same prices. 1 credit per business; reviews and photos beyond the free first page cost 10 items = 1 credit. No surcharge for using MCP. The assistant is told to warn you before starting anything larger than about 1,000 businesses.
- Same limits. 60 requests per minute per key, 5 searches running at once.
- Built for assistants. Every tool declares an output schema, and every result carries a
summaryof what happened and anext_stepnaming the single follow-up call with the id already filled in. That is what keeps the assistant from guessing its way through a multi-step job.
Endpoints
| Endpoint | What it does |
|---|---|
| POST/v1/searches | Start a search. Returns the search object with status: "queued". |
| GET/v1/searches | List your searches, newest first. Query: limit (1 to 100), page, status. |
| GET/v1/searches/{id} | Status, progress, result count and credits of one search. |
| GET/v1/searches/{id}/results | Download records. Query: format = json, ndjson, csv, xlsx, xml; after_id for NDJSON streaming. |
| POST/v1/searches/{id}/cancel | Cancel a search that has not started yet. Reserved credits are released. |
| GET/v1/account | Credit balance, current prices and your limits. |
Start a search
POST/v1/searches
JSON body. Only location and keyword are required.
| Field | Type | Description |
|---|---|---|
| location | string, required | Where to search. City, region or country, e.g. "Austin, TX, USA", "Berlin, Germany", "Manchester, UK". |
| keyword | string, required | What to search for, as you would type it on Google Maps: "dentist", "coffee shop", "car rental". |
| max_results | integer | Upper bound on businesses. Omit it to collect everything the area yields, limited only by your credits. Any value from 1 upwards. |
| language | string | Google Maps language code for names and categories, default en. Examples: de, fr, es, tr. |
| max_reviews_per_place | integer, 0 to 5000 | Reviews to collect for each business, newest first. Default 0 keeps the free first page only. |
| max_images_per_place | integer, 0 to 5000 | Photos to collect for each business. Default 0 keeps the free first page only. |
| reviews_since | date, YYYY-MM-DD | Only collect reviews newer than this date. |
| max_credits | integer | Spending cap for this search. The search stops before it would exceed this amount. |
status: "queued" and starts automatically after a top-up.The search object
{
"id": "24b3ca0c-1f3a-4111-9ff5-31feb4e51660",
"status": "completed", // queued · running · completed · failed · cancelled
"location": "Eskişehir",
"keyword": "pharmacy",
"query": "Eskişehir, Turkey pharmacy",
"language": "en",
"max_results": 20,
"max_reviews_per_place": 15,
"max_images_per_place": 0,
"reviews_since": null,
"max_credits": null,
"results_count": 20,
"credits": { "reserved": 50, "charged": 27 }, // charged is null until the search finishes
"created_at": "2026-09-11T06:40:43Z",
"started_at": "2026-09-11T06:41:02Z",
"completed_at": "2026-09-11T06:42:18Z",
"error": null
}
- queued: accepted, waiting for a worker or for credits. running: collecting. completed: results ready. failed: something went wrong, nothing charged for missing records.
errorcarries acodeand amessagewhen the search is waiting for credits or has failed.
Results and formats
GET/v1/searches/{id}/results?format=json
Each record is a full business profile with 100+ fields: name, category, address, coordinates, phone, website, opening hours, rating and review count, price level, verified email, social profiles (Facebook, Instagram, LinkedIn, X, TikTok, YouTube and more), website technology signals and, when requested, the reviews and photos. See what's in a record.
| format | Available | Notes |
|---|---|---|
| json | completed | A JSON array of nested records. Default. |
| ndjson | while running and after | One record per line. Each line has _id (a cursor) and cid (the business's Google id). Pass the last _id as after_id to fetch only new records. |
| csv | completed | Flattened, one row per business, nested fields as columns. |
| xlsx | completed | Excel workbook, same layout as the dashboard export. |
| xml | completed | Nested XML. |
Streaming a running search:
GET /v1/searches/{id}/results?format=ndjson&after_id=0 → first batch, remember the last _id
GET /v1/searches/{id}/results?format=ndjson&after_id=48213 → only records written since
Pricing and credits
The API uses the same credits as the dashboard, with no extra fee.
- 1 credit = 1 business record. All 100+ fields included.
- Reviews and photos: the first page is always included. Beyond it, 10 reviews = 1 credit and 10 photos = 1 credit, per business.
- No minimum per search. Ask for 5 businesses or 50,000.
- No results, no charge. Credits never expire.
Check your balance any time with GET /v1/account. Buy credits in the dashboard.
Errors
Errors use HTTP status codes and a JSON body:
{ "error": { "code": "insufficient_credits", "message": "Not enough credits to start a search. Available: 0. Buy credits in your dashboard." } }
| HTTP | code | Meaning |
|---|---|---|
| 401 | unauthorized, invalid_api_key, api_key_revoked | Missing, wrong or revoked key. |
| 402 | insufficient_credits | Not enough available credits to start a search. |
| 403 | account_disabled | The account is disabled. |
| 404 | not_found | No search with that id on your account. |
| 409 | search_not_finished, cannot_cancel | Results requested before completion, or cancelling a search that already started. |
| 422 | validation_error | A field is missing or invalid. details lists each field. |
| 429 | rate_limited, too_many_active_searches | Slow down (see Retry-After), or wait for a running search to finish. |
| 500 | server_error | Something broke on our side. Retry later; nothing is charged for missing records. |
Rate limits
- 60 requests per minute per API key. Above that you get
429 rate_limitedwith aRetry-Afterheader. Polling a search every 5 to 10 seconds stays far below the limit. - 5 searches running at the same time per account. Start the next one when one finishes.
- There is no limit on the number of businesses or credits.
Code examples
Python
import requests, time
API = "https://api.businessmonster.co/v1"
H = {"Authorization": "Bearer bm_live_YOUR_KEY"}
search = requests.post(f"{API}/searches", headers=H, json={
"location": "Austin, TX, USA", "keyword": "dentist", "max_results": 500,
}).json()
while search["status"] in ("queued", "running"):
time.sleep(10)
search = requests.get(f"{API}/searches/{search['id']}", headers=H).json()
if search["status"] == "completed":
rows = requests.get(f"{API}/searches/{search['id']}/results",
headers=H, params={"format": "json"}).json()
print(len(rows), "businesses,", search["credits"]["charged"], "credits")
Node.js
const API = "https://api.businessmonster.co/v1";
const H = { Authorization: "Bearer bm_live_YOUR_KEY", "Content-Type": "application/json" };
let search = await (await fetch(`${API}/searches`, {
method: "POST", headers: H,
body: JSON.stringify({ location: "Berlin, Germany", keyword: "coffee shop", max_results: 300 }),
})).json();
while (["queued", "running"].includes(search.status)) {
await new Promise(r => setTimeout(r, 10000));
search = await (await fetch(`${API}/searches/${search.id}`, { headers: H })).json();
}
const rows = await (await fetch(`${API}/searches/${search.id}/results?format=json`, { headers: H })).json();
console.log(rows.length, "businesses");
Streaming while the search runs (Python)
import json
after = 0
while True:
r = requests.get(f"{API}/searches/{search['id']}/results",
headers=H, params={"format": "ndjson", "after_id": after}, stream=True)
for line in r.iter_lines():
rec = json.loads(line)
after = rec["_id"]
handle(rec) # your code
status = requests.get(f"{API}/searches/{search['id']}", headers=H).json()["status"]
if status not in ("queued", "running"):
break
time.sleep(10)
Questions? Write to us from the dashboard's Support page, or open the interactive reference to try every endpoint in the browser.