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.

Base URL 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…

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.

Server https://api.businessmonster.co/mcpStreamable HTTPStatelessProtocol 2024-11-05 to 2026-07-28

Connecting

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
Treat that URL like a password: anyone holding it can spend your credits. We keep it out of our access logs, and you can revoke the key from your dashboard at any time, which kills the connection instantly.

Tools the assistant gets

ToolWhat it does
search_businessesStarts a search from a location and a keyword. Returns a search id straight away. Spends credits.
get_searchStatus and progress. Takes wait_seconds (up to 45) so the assistant waits on the server instead of polling in a loop.
get_search_resultsA page of compact records (name, category, address, phone, website, email, rating, socials). Ask for specific fields, a page size, or the last N.
cancel_searchCancels a search that has not started yet and releases its reserved credits.
list_searchesYour recent searches with status and credit cost.
get_accountCredit balance, current prices and limits.
list_available_fieldsThe fields a business record can contain, grouped by topic, so the assistant can answer "can you get their Instagram?" without guessing.

What to expect

Endpoints

EndpointWhat it does
POST/v1/searchesStart a search. Returns the search object with status: "queued".
GET/v1/searchesList 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}/resultsDownload records. Query: format = json, ndjson, csv, xlsx, xml; after_id for NDJSON streaming.
POST/v1/searches/{id}/cancelCancel a search that has not started yet. Reserved credits are released.
GET/v1/accountCredit balance, current prices and your limits.

Start a search

POST/v1/searches

JSON body. Only location and keyword are required.

FieldTypeDescription
locationstring, requiredWhere to search. City, region or country, e.g. "Austin, TX, USA", "Berlin, Germany", "Manchester, UK".
keywordstring, requiredWhat to search for, as you would type it on Google Maps: "dentist", "coffee shop", "car rental".
max_resultsintegerUpper bound on businesses. Omit it to collect everything the area yields, limited only by your credits. Any value from 1 upwards.
languagestringGoogle Maps language code for names and categories, default en. Examples: de, fr, es, tr.
max_reviews_per_placeinteger, 0 to 5000Reviews to collect for each business, newest first. Default 0 keeps the free first page only.
max_images_per_placeinteger, 0 to 5000Photos to collect for each business. Default 0 keeps the free first page only.
reviews_sincedate, YYYY-MM-DDOnly collect reviews newer than this date.
max_creditsintegerSpending cap for this search. The search stops before it would exceed this amount.
A search never charges more than it finds. Credits are reserved when it starts and settled to the real amount when it finishes. If there are not enough credits, the search waits with 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
}

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.

formatAvailableNotes
jsoncompletedA JSON array of nested records. Default.
ndjsonwhile running and afterOne 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.
csvcompletedFlattened, one row per business, nested fields as columns.
xlsxcompletedExcel workbook, same layout as the dashboard export.
xmlcompletedNested 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.

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." } }
HTTPcodeMeaning
401unauthorized, invalid_api_key, api_key_revokedMissing, wrong or revoked key.
402insufficient_creditsNot enough available credits to start a search.
403account_disabledThe account is disabled.
404not_foundNo search with that id on your account.
409search_not_finished, cannot_cancelResults requested before completion, or cancelling a search that already started.
422validation_errorA field is missing or invalid. details lists each field.
429rate_limited, too_many_active_searchesSlow down (see Retry-After), or wait for a running search to finish.
500server_errorSomething broke on our side. Retry later; nothing is charged for missing records.

Rate limits

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.