Free100Leads
API
Last updated 19 August 2026
Getting a key
Keys are created in your account and shown once. We store a hash, so nobody can look one up later, including us. Lose it and you create another. Revoking is immediate.
A key looks like f100_live_…. That prefix is fixed so secret scanners can recognise it if it ever reaches a public repository. Treat it as a password: server side only, never in a browser, never in a mobile app.
Making a request
Everything lives under https://api.free100leads.com/v1. Send the key as a bearer token.
curl "https://api.free100leads.com/v1/search?country=DE&industry_id=4&limit=100" \ -H "Authorization: Bearer f100_live_..."
Searching
GET /v1/search needs at least one of country, industry_id or size_bucket. That is the same floor the website has: there is no way to ask for everything.
| Parameter | Meaning |
|---|---|
| country | ISO-3166 alpha-2, e.g. DE |
| region | US state name, lowercase. Only meaningful with country=US |
| industry_id | From /v1/facets |
| size_bucket | 1 (1-10) through 8 (10001+) |
| contact | email or phone, to require that channel |
| title | Job title contains this text |
| limit | Up to 500 a page |
| cursor | From the previous response |
{
"data": [
{
"name": "…",
"job_title": "…",
"company": "…",
"email": "…",
"phone": "…",
"city": "…",
"region": null,
"country": "DE",
"industry": "computer software",
"company_size": "51-200"
}
],
"next_cursor": "c_…",
"exhausted": false,
"usage": { "records_used": 4210, "records_included": 100000, "period_end": "…" }
}Paging, and why there is no page number
Keep the next_cursor and pass it back to continue. You are never handed the same record twice within a search, until exhausted comes back true.
The cursor is signed and bound to your account and to the exact filters you used. One issued for a German search will be refused for a French one, and one issued to another account will be refused for yours. Start a new search by leaving it out.
There is deliberately no way to fetch a record by id, and no offset parameter. Both would let somebody walk the whole database in parallel, which is what this design exists to prevent.
The other endpoints
| Endpoint | What it gives you |
|---|---|
| GET /v1/facets | Every country, industry and size you can filter on, with counts |
| GET /v1/usage | Records used and left this period, and today |
| GET /v1/me | Which key this is, which plan, and its limits |
Not being sold the same contact twice
Two different things stop a record reaching you twice.
Inside one search, the cursor is the guarantee. The walk only ever moves forward, so paging through a search cannot hand back a record it has already handed you, however many pages you take. It needs nothing from you beyond passing next_cursor back.
Across exports, a ledger is the guarantee. Every row that leaves as a CSV row is recorded against your account. Later exports skip it, and /v1/search stops returning it, so a contact you have already taken away does not reappear in a search two months later, and does not land in your CRM a second time.
Records count against your plan when they are delivered: on screen, in an API response, or in a bulk file. The suppression ledger is filled by exports, not searches. The bulk endpoint always skips records already in the ledger.
A record is written to the ledger when it leaves as a file, not when it appears in a search response. Within one search the cursor guarantees no repeats. Across two searches with overlapping filters, a record you saw but never exported can come round again. exhausted marks the end of a search.
The ledger belongs to the account rather than to a key, so every key and the web app read and write the same one, and it does not expire: a record exported in March is still suppressed in December. If you need a list back after losing it, the CSV download on your dashboard takes include_exported=true, and that does not charge you again. Those records were counted the first time they were delivered.
Limits
These numbers are published here so you never discover them in production.
| Limit | Growth | Scale |
|---|---|---|
| Records a month | 100,000 | 1,500,000 |
| Records a day | 10,000 | 50,000 |
| Records an hour | 5,000 | 25,000 |
| Requests a minute | 25 | 60 |
| Requests in flight | 2 | 4 |
| Keys | 3 | 10 |
Every response carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset. A 429 also carries Retry-After in seconds. Usage counts the records you actually received, never the ones you asked for.
Daily limits ramp up to the plan's full rate over your first week. Monthly totals are never reduced.
Errors
{
"error": {
"type": "rate_limited",
"message": "60 requests a minute is the limit on this plan.",
"retry_after": 12,
"docs": "https://free100leads.com/docs/api#rate_limited"
}
}| Type | Status | What to do |
|---|---|---|
| invalid_request | 400 | Fix the parameters. Retrying unchanged will not help |
| unauthorized | 401 | The key is wrong, revoked or missing |
| quota_exhausted | 402 | Out of records. Deliberately not a 429, because backing off will not fix a billing state. Wait for the reset or upgrade |
| forbidden | 403 | The plan or the key does not carry this |
| not_found | 404 | No such endpoint or resource. Check the path |
| rate_limited | 429 | Slow down. Honour Retry-After |
| server_error | 500 | Ours. Retry with backoff, and tell us if it persists |
What you may do with the data
Records are licensed for your own outreach. Do not resell them, republish them, or fold them into a product you sell. Delivered records carry traceable markers tied to the key that fetched them, so a list that turns up somewhere else can be traced back to the account it came from. The terms state this in full.
MCP
Point Claude, Codex or any other MCP-aware agent at https://api.free100leads.com/v1/mcp with the same key. It speaks JSON-RPC over HTTP and offers three tools: search_leads, get_facets and get_usage. They run the same code the endpoints above run, so an agent gets the same allowance, the same rate limits and the same cursor rules. There is no separate quota to keep track of.
Most MCP clients take a config block like this one, with your own key in place of the placeholder:
{
"mcpServers": {
"free100leads": {
"url": "https://api.free100leads.com/v1/mcp",
"headers": { "Authorization": "Bearer f100_live_..." }
}
}
}Webhooks
Save a search, attach an endpoint, and new matches are posted to it as they land. Every delivery carries x-f100-timestamp and x-f100-signature. Check the signature against the secret shown when you created the endpoint, and reject anything with a timestamp more than five minutes old. A receiver that cannot tell our POST from anybody else's has an open endpoint.
Failures back off and retry. Ten consecutive failures switch the endpoint off and the error appears on your account page, so a dead URL stops being retried forever.
What is deliberately missing
Enrichment, meaning looking a person up by email, is not offered. Direct lookup of a specific individual would undo the anti-abuse design of the whole API.
Missing something you need? The contact form reaches a person.