Search contacts, straight from your agent.
One endpoint for finding business contacts by industry, city, or country — your own vocabulary, not low-level pass-through parameters.
Overview
The B2BLeads API is a single MCP-friendly discovery endpoint. Base URL:
https://api.b2bleadsapi.comEvery response is JSON. There is no SDK required — plain HTTPS requests work from any language or from an AI agent's native tool-calling.
Authentication
Pass your API key as a bearer token. Create keys from the dashboard under API Keys.
Authorization: Bearer b2bl-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxRequests without a valid key return 401 Unauthorized. Each key is rate-limited per minute and capped by your plan's monthly search quota.
GET /v1/search-leads
Finds companies matching your own search vocabulary — clean, purpose-built options rather than low-level request fields. At least one of q, industry, or city is required.
Query parameters
| Parameter | Type | Description |
|---|---|---|
| q | string | Free-text query, e.g. "family-owned bakeries". Optional if industry or city is set. |
| industry | enum | One of our own industry values — see GET /v1/search-leads/industries for the full list. |
| city | string | City or region name, e.g. "Munich" or "Austin, TX". Combine with radius_km for a bounded search. |
| radius_km | number | Search radius around city, in kilometers (max 50). Ignored without city. |
| min_rating | number 0–5 | Only return companies with at least this rating, filtered server-side for accurate pagination. |
| has_website | boolean | true to only return companies with a listed website. Deprecated alias for website_presence=present; prefer website_presence. |
| website_presence | enum | present, absent, or any (default). "absent" returns companies with no listed website, e.g. for "dentists without a website". |
| max_review_count | number | Only return companies with at most this many reviews, filtered server-side. Use 0 for "without reviews". |
| verified_only | boolean | true to exclude permanently/temporarily closed businesses. |
| open_now | boolean | true to only return businesses that are currently open. |
| price_level | enum, comma-separated | One or more of: budget, moderate, expensive, luxury. E.g. price_level=budget,moderate. |
| rank_by | enum | relevance (default) or distance. distance requires city to be set. |
| limit | number 1–20 | Max results per page. Defaults to 20 (per-request cap). |
| page_token | string | Value from a previous response's nextPageToken, to fetch the next page. When set, all other filters are ignored (already baked into the token). |
| lang | string | Language for result names/addresses, e.g. "pt" or "pt-BR". Overrides your account's dashboard language for this request; falls back to it when omitted. |
| list_id | string | Save these results into an existing saved list you own. Adds a savedTo object to the response. |
| list_name | string | Save into a list with this name, creating it if it doesn't exist yet. Ignored if list_id is also set. |
| save_to_list | boolean | true saves into a list named "API searches" (created on first use) when neither list_id nor list_name is given. |
Example request
curl "https://api.b2bleadsapi.com/v1/search-leads?industry=construction&city=Munich&radius_km=25&min_rating=4&has_website=true&open_now=true&rank_by=distance" \
-H "Authorization: Bearer b2bl-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"Example response
{
"results": [
{
"id": "ChIJ...",
"name": "Bauunion GmbH",
"address": "Landsberger Str. 12, 80339 München",
"phone": "+49 89 1234567",
"website": "https://bauunion.de",
"rating": 4.7,
"ratingCount": 128,
"status": "OPERATIONAL",
"priceLevel": null,
"openNow": true
}
],
"nextPageToken": "AY1v..."
}Pass nextPageToken back as page_token to fetch the next page. It's null once there are no more results.
Note for AI agents: on plans with Email Finder, a saved contact's email field can come back as the literal string CFD* instead of an address. This means every scrape attempt (a plain HTTP fetch, then a headless-browser fallback) was blocked by the target site's Cloudflare bot protection — it is not an empty result and not an error, and retrying the same site will not succeed. Treat CFD* as "no email available" rather than as a real address, and don't report it to the end user as their contact email.
Try it live
Sent directly from your browser to the live API — this counts against your quota. Your key stays in this browser tab only (sessionStorage), cleared when you close it, and never sent anywhere but api.b2bleadsapi.com.
This counts as 1 search against your quota regardless of the limit you set — quota is charged per call, not per result returned.
GET /v1/search-leads/industries
Public, unauthenticated endpoint — returns the current list of supported industry values, so clients and agents never need to hardcode them.
curl "https://api.b2bleadsapi.com/v1/search-leads/industries"{
"industries": [
{ "value": "construction", "label": "Construction" },
{ "value": "restaurants", "label": "Restaurants" },
{ "value": "retail", "label": "Retail" },
{ "value": "manufacturing", "label": "Manufacturing" },
{ "value": "logistics", "label": "Logistics & Freight" },
{ "value": "it_services", "label": "IT Services" },
{ "value": "healthcare", "label": "Healthcare" },
{ "value": "real_estate", "label": "Real Estate" },
{ "value": "hospitality", "label": "Hospitality" },
{ "value": "professional_services", "label": "Professional Services" },
{ "value": "legal_services", "label": "Legal Services" },
{ "value": "accounting_finance", "label": "Accounting & Finance" },
{ "value": "banking_financial", "label": "Banking & Financial Services" },
{ "value": "insurance", "label": "Insurance" },
{ "value": "automotive", "label": "Automotive" },
{ "value": "education", "label": "Education & Training" },
{ "value": "dental", "label": "Dental" },
{ "value": "veterinary", "label": "Veterinary" },
{ "value": "beauty_wellness", "label": "Beauty & Wellness" },
{ "value": "fitness", "label": "Fitness & Recreation" },
{ "value": "moving_storage", "label": "Moving & Storage" },
{ "value": "pharmacy", "label": "Pharmacy" },
{ "value": "marketing_advertising", "label": "Marketing & Advertising" },
{ "value": "telecommunications", "label": "Telecommunications" },
{ "value": "energy_utilities", "label": "Energy & Utilities" },
{ "value": "agriculture", "label": "Agriculture" },
{ "value": "wholesale_distribution", "label": "Wholesale & Distribution" },
{ "value": "event_services", "label": "Event Services" }
]
}Try it live
Public endpoint, no API key needed.
Errors
Errors are plain JSON with a single error field.
| Parameter | Type | Description |
|---|---|---|
| 400 | Bad Request | Missing all of q/industry/city, unknown industry/price_level/rank_by value, rank_by=distance without city, or an out-of-range radius_km/min_rating/limit. |
| 401 | Unauthorized | Missing or invalid API key. |
| 402 | Payment Required | Monthly search quota exceeded for this key's plan. |
| 403 | Forbidden | Account is suspended or banned. |
| 429 | Too Many Requests | Per-key rate limit exceeded — back off and retry. |
| 502 | Bad Gateway | Upstream discovery provider failed; safe to retry. |
| 503 | Service Unavailable | Contact search is temporarily disabled via feature flag. |
Rate limits & quota
Each API key has a per-minute rate limit and a monthly search quota, both set by your plan. Check current usage anytime from the dashboard's Billing page, or via the GET /v1/billing endpoint if you're authenticated with a session token.
Using it as an MCP tool
We publish an official MCP server that wraps this API. The local stdio package (Claude Desktop, Claude Code, Cursor, and other MCP clients that launch a local process) exposes six tools — search_leads, search_leads_advanced, list_industries, list_saved_lists, get_saved_list, and find_email. The hosted OAuth endpoint (ChatGPT, Gemini/Antigravity, Grok Build, GitHub Copilot) exposes four: search_leads, search_leads_advanced, list_industries, and find_email — read-only, without saved-list management. No manual REST integration needed either way.
Add it to your MCP client config with an API key from the API Keys:
{
"mcpServers": {
"b2bleads": {
"command": "npx",
"args": ["-y", "b2bleads-mcp"],
"env": {
"B2BLEADS_API_KEY": "your-api-key-here"
}
}
}
}Prefer to wire it up yourself over plain REST? The parameters above map directly to the tool's input schema — the agent picks an industry and city in plain language, and B2BLeads takes care of translating that into a live discovery query behind the scenes.
{
"name": "search_leads",
"description": "Find companies by industry, city and radius",
"parameters": {
"q": "string, optional free-text query",
"industry": "enum, see /v1/search-leads/industries",
"city": "string",
"radius_km": "number, max 50",
"min_rating": "number 0-5",
"has_website": "boolean, deprecated — use website_presence",
"website_presence": "enum: any, present, absent",
"max_review_count": "number, e.g. 0 for \"without reviews\"",
"verified_only": "boolean",
"open_now": "boolean",
"price_level": "enum, comma-separated: budget, moderate, expensive, luxury",
"rank_by": "enum: relevance, distance (requires city)",
"limit": "number 1-20",
"page_token": "string, from a previous response's nextPageToken"
}
}A separate tool scans a wider area of a city in a grid of sub-searches for broader coverage — but it carries an explicit elevated-cost warning so agents don't call it by default:
{
"name": "search_leads_advanced",
"description": "⚠️ Elevated cost: 9x the quota of a single search_leads call. Tiles a city into a 3x3 grid of sub-searches to return up to ~180 deduplicated businesses in one city, beyond search_leads' normal ~20-60 result ceiling. Only use when more results are explicitly needed for one city — never as a default. Does not support pagination.",
"parameters": {
"q": "string, optional free-text query",
"industry": "enum, see /v1/search-leads/industries",
"city": "string, required — ONLY the city name, e.g. \"Lyon\" (not the full search query)",
"min_rating": "number 0-5",
"has_website": "boolean, deprecated — use website_presence",
"website_presence": "enum: any, present, absent",
"max_review_count": "number, e.g. 0 for \"without reviews\"",
"verified_only": "boolean",
"open_now": "boolean",
"price_level": "enum, comma-separated: budget, moderate, expensive, luxury"
}
}Additional read-only tools round out the MCP surface:
{
"name": "list_industries",
"description": "List the industry values accepted by search_leads",
"parameters": {}
}
{
"name": "list_saved_lists",
"description": "List this account's saved lead lists",
"parameters": {}
}
{
"name": "get_saved_list",
"description": "Get the leads saved in a specific list",
"parameters": {
"list_id": "string, required — from list_saved_lists"
}
}
{
"name": "find_email",
"description": "Look up a best-effort contact email for a single business website",
"parameters": {
"website": "string, required, e.g. https://example.com"
}
}find_email requires any paid plan (Starter and above) — the same tier that already auto-enriches search_leads results with emails. Keys with no active plan get a plain "upgrade required" error instead of a result.