getLeads API and MCP server
Search for B2B leads, check prices and unlock leads from your own code, or let an AI assistant do it through MCP. The API uses your account: the same wallet, free unlocks, prices and daily limits as the website.
Authentication
Create a key in the app under Profile Settings, Password & Security, API keys. You see the key once, so store it in a secret manager or an environment variable. Up to 5 keys per account; revoke one at any time and it stops working at once. Live keys start with gl_live_; test keys (gl_test_) only work on test deployments.
Authorization: Bearer gl_live_your_key_here Send JSON with Content-Type: application/json. Never put a key in a browser, a mobile app or a public repository: anyone with it can spend your credit. You can set a daily spend cap per key.
Endpoints
Base URL: https://getleads.site/api/v1
| Method | Path | What it does |
|---|---|---|
| GET | /api/v1/me | Your account, wallet balance (USD cents) and free unlocks left. |
| GET | /api/v1/pricing?country=KE | Price tiers, wallet packs and the exact per-lead price for a country. |
| POST | /api/v1/leads/search | Start a lead search (free; uses one daily search). Answers 202 with a job. |
| GET | /api/v1/jobs/{id} | Poll a search or scan job: running, succeeded (with results) or failed. |
| GET | /api/v1/leads?limit=25&cursor= | Your unlocked leads, newest first. Follow nextCursor for more. |
| GET | /api/v1/leads/{id} | One lead: full if you own it, otherwise the locked preview (with its price when unlocking it costs money). |
| POST | /api/v1/leads/{id}/unlock | Unlock a lead. Charges your wallet or uses a free unlock. Needs an Idempotency-Key header. |
| POST | /api/v1/domain/scan | Scan a company website for target niches and buyer roles. Answers 202 with a job. |
| GET | /api/v1/domain/scan/{id} | Status of a domain scan (same as /api/v1/jobs/{id}). |
| GET | /api/v1/openapi.json | The OpenAPI 3.1 description of all of the above. No key needed. |
Searches return locked previews: niche, country, fit score and, when unlocking costs money, the price; no company name or links. Unlocking a lead returns the company, its checked website and LinkedIn page, the suggested decision-maker role and an opening line. Leads you have unlocked stay yours; unlocking one again never charges twice. There are no payment or admin endpoints: top up on the website. Full schema: openapi.json.
Examples
curl
export GETLEADS_API_KEY=gl_live_...
# Balance
curl -s https://getleads.site/api/v1/me -H "Authorization: Bearer $GETLEADS_API_KEY"
# Start a search, then poll the job it returns
curl -s -X POST https://getleads.site/api/v1/leads/search \
-H "Authorization: Bearer $GETLEADS_API_KEY" -H "Content-Type: application/json" \
-d '{"niche":"Logistics","location":"Kenya","count":10}'
curl -s https://getleads.site/api/v1/jobs/job_... -H "Authorization: Bearer $GETLEADS_API_KEY"
# Unlock one lead (safe to retry with the same Idempotency-Key)
curl -s -X POST https://getleads.site/api/v1/leads/LEAD_ID/unlock \
-H "Authorization: Bearer $GETLEADS_API_KEY" -H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" -d '{"payWith":"credit"}'JavaScript (Node 18+)
const base = 'https://getleads.site/api/v1';
const headers = {Authorization: `Bearer ${process.env.GETLEADS_API_KEY}`, 'Content-Type': 'application/json'};
const start = await fetch(`${base}/leads/search`, {method: 'POST', headers, body: JSON.stringify({niche: 'Logistics', location: 'KE'})});
let {job} = await start.json();
while (job.status === 'running' || job.status === 'queued') {
await new Promise((r) => setTimeout(r, 5000));
({job} = await (await fetch(`${base}/jobs/${job.id}`, {headers})).json());
}
const lead = job.result.leads.find((l) => l.unlockable);
const res = await fetch(`${base}/leads/${lead.id}/unlock`, {
method: 'POST',
headers: {...headers, 'Idempotency-Key': crypto.randomUUID()},
body: JSON.stringify({payWith: 'credit'}),
});
const body = await res.json();
if (!res.ok) throw new Error(`${body.error.code}: ${body.error.message}`);
console.log(body.lead.company, body.lead.website, body.chargedCents);Python (requests)
import os, time, uuid, requests
BASE = "https://getleads.site/api/v1"
s = requests.Session()
s.headers["Authorization"] = f"Bearer {os.environ['GETLEADS_API_KEY']}"
job = s.post(f"{BASE}/leads/search", json={"niche": "Logistics", "location": "Kenya"}).json()["job"]
while job["status"] in ("queued", "running"):
time.sleep(5)
job = s.get(f"{BASE}/jobs/{job['id']}").json()["job"]
lead = next(l for l in job["result"]["leads"] if l["unlockable"])
r = s.post(f"{BASE}/leads/{lead['id']}/unlock", json={"payWith": "credit"},
headers={"Idempotency-Key": str(uuid.uuid4())})
if r.status_code == 429:
time.sleep(int(r.headers.get("Retry-After", "5")))
r.raise_for_status()
print(r.json()["lead"]["company"])Errors
Every error has the same shape. Branch on code; the message is for people.
{
"error": {
"code": "insufficient_credit",
"message": "Not enough credit to unlock this lead. Top up on the website. Nothing was charged."
}
}| Status | Codes | Meaning |
|---|---|---|
| 400 | invalid_request, idempotency_key_required, unknown_country | The request is missing something or has a wrong value. Fix it before retrying. |
| 401 | missing_api_key, invalid_api_key, revoked_api_key, wrong_environment | No key, an unknown or revoked key, or a test key on the live site. |
| 402 | insufficient_credit, no_free_unlocks | Not enough credit. Top up on the website; nothing was charged. |
| 403 | account_suspended, daily_spend_cap_reached, email_not_verified | The account or key may not do this right now. |
| 404 | not_found, lead_not_found, job_not_found | No such endpoint, lead or job on your account. |
| 409 | already_unlocked, not_unlockable, idempotency_in_progress | Already yours, cannot be sold (no checked website), or the same request is still running. |
| 422 | idempotency_key_reused | That Idempotency-Key was used for a different request. |
| 429 | rate_limited | Too many requests. Wait for the number of seconds in the Retry-After header. |
| 503 | kill_switch, unavailable | Searches and free unlocks are paused, or the daily capacity is used up. Nothing was charged. |
Limits and retries
- 60 requests per minute per key; lead searches and domain scans 6 per minute per key.
- Your account's daily limits apply too (the same as the website: searches and domain scans per day, reset at 00:00 UTC).
- A 429 answer carries
Retry-Afterin seconds. Wait that long; do not retry in a tight loop. POST /leads/{id}/unlockneeds anIdempotency-Keyheader (for example a UUID). A retry with the same key returns the first answer and never charges twice. Errors are not stored, so after a top-up you can retry with the same key.- Searches take 20 to 60 seconds. Poll the job every 5 seconds; a job that has not finished after 3 minutes is reported as failed and you are not charged.
- Optional per-key daily spend cap (set it when you create the key). Unlocks over the cap answer 403
daily_spend_cap_reached.
Pricing
The API costs nothing extra. Searching is free within your daily limits; you pay only for leads you unlock, at the same per-country price as on the pricing page, from the same prepaid credit. Free unlocks from a verified account can be used through the API too (send payWith: "credit" to keep them). A free unlock costs nothing, so no price is shown for it: priceUsd is left out of a lead you unlocked for free and of locked leads your free unlocks will pay for, and a free unlock's response has no chargedCents. Paid unlocks always show the price. If a lead you unlocked fails its checks, its price goes back to your balance, as on the website. Use of the API is covered by the Terms of Service and the email compliance rules.
MCP server for AI assistants
getLeads runs a remote MCP server (streamable HTTP) at https://getleads.site/api/mcp, using the same API key. Tools: get_balance, get_pricing, search_leads, get_job, list_leads, unlock_lead and scan_domain. unlock_lead spends money, so it only runs with confirm: true; the assistant is told to show you the price (or tell you it uses one of your free unlocks) and ask first. A search can take up to a minute; if it is still running the tool returns a job id and the assistant checks it with get_job.
Cursor
Add to ~/.cursor/mcp.json (or .cursor/mcp.json in a project you do not commit secrets from):
{
"mcpServers": {
"getleads": {
"url": "https://getleads.site/api/mcp",
"headers": {
"Authorization": "Bearer gl_live_your_key_here"
}
}
}
}Claude Desktop
Claude Desktop's custom connectors expect OAuth, so connect through the mcp-remote bridge (needs Node.js). In Settings, Developer, Edit Config (claude_desktop_config.json):
{
"mcpServers": {
"getleads": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://getleads.site/api/mcp",
"--header",
"Authorization:${GETLEADS_AUTH}"
],
"env": {
"GETLEADS_AUTH": "Bearer gl_live_your_key_here"
}
}
}
}Hermes Agent (Nous Research)
Put the key in ~/.hermes/.env as GETLEADS_API_KEY=gl_live_..., then add to ~/.hermes/config.yaml:
mcp_servers:
getleads:
url: "https://getleads.site/api/mcp"
headers:
Authorization: "Bearer ${GETLEADS_API_KEY}"
timeout: 120 Any other MCP client: point it at the URL above with the header Authorization: Bearer gl_live_.... The server is stateless (no session id, no server-sent events); a GET answers 405 as the protocol allows.
Get your API key
Sign in, open Profile Settings, Password & Security, and create a key. Searching is free; you pay only for leads you unlock.