# API Rate Limits
Source: https://doc.bettercontact.rocks/api-reference/api_rate_limits
How many requests per minute you can make, and what happens when you go over
Rate limits are counted **per API key**, across all endpoints together rather than per endpoint.
Going over returns `429 Too Many Requests`.
## Limits
| Plan | Limit |
| - | - |
| Starter, free and legacy accounts under 1 000 monthly credits | **100 requests / minute** |
| Pro and accounts from 1 000 monthly credits | **200 requests / minute** |
| Higher volumes | On request, [contact us](mailto:contact@bettercontact.rocks) |
[`GET /account`](/api-reference/endpoint/account) is exempt from these limits, so you can check
your balance without spending your budget on it.
Requests carrying an unknown or deactivated API key are throttled much harder, at 5 requests per
5 minutes per IP. If you are hitting that, fix the key rather than retrying.
## Handling 429
The response is a plain `429` with the body `Retry later`. Back off exponentially and retry: unlike
`400` or `422`, a `429` request is worth replaying as is.
A client stuck in a retry loop that keeps firing at full speed can get the account **blocked**, not
just throttled. Always put a real backoff between retries, and cap the number of attempts.
## Staying under the limit
* Enrich in batches. `POST /async` takes up to 100 leads in a single call, which is one request
instead of a hundred.
* Use a [webhook](/api-reference/webhooks) instead of polling. Polling a large batch every second is
the most common way to hit the limit for nothing.
* When you do poll, wait a few seconds between attempts. See the recipe in
[Request statuses](/api-reference/statuses).
# Authentication
Source: https://doc.bettercontact.rocks/api-reference/authentication
One API key, one header, on every endpoint
Every endpoint is authenticated with the **`X-API-Key`** header.
```bash theme={null}
curl https://app.bettercontact.rocks/api/v2/account \
-H "X-API-Key: YOUR_API_KEY"
```
It is **not** an `Authorization: Bearer` header. Sending the key that way returns
[`401`](/api-reference/errors).
Find your API key [here](https://app.bettercontact.rocks/api_requests) in your BetterContact
account. You can regenerate or deactivate it from the same screen.
## Base URL
```
https://app.bettercontact.rocks/api/v2
```
## Scope of a key
A key identifies one user, but credits are pooled at the organisation level. Two keys belonging to
the same organisation draw from the same balance, and the [rate limit](/api-reference/api_rate_limits)
is applied per key.
A `request_id` is only readable with the key that created it. Polling someone else's request returns
`406` on the enrichment API and `404` on the Lead Finder API, not `401`.
## Keep the key server side
The key grants full access to your credits. Never ship it in front-end code, a mobile app, or a
public repository. If it leaks, regenerate it from your API settings: the old key stops working
immediately.
# Credits
Source: https://doc.bettercontact.rocks/api-reference/credits
When credits are consumed, and what happens when you run out
## When a credit is consumed
Credits are consumed **per valid data point returned**, not per API call and not per provider tried.
* A lead whose email comes back `deliverable` consumes credits.
* A lead whose email comes back `undeliverable`, or for which nothing was found, does not.
* The same rule applies to mobile phone numbers: a number that fails verification is not charged.
* The waterfall may query several providers for a single lead. You are charged once for the result,
not once per provider.
* A Lead Finder search on its own does not consume enrichment credits. Setting
`enrich_email_address` or `enrich_phone_number` on the search does.
* A [Company Finder](/api-reference/endpoint/company_finder_post) search costs 0.1 credit per
company returned. Each page of a paginated search is charged on its own, and a search that
matched nothing is free.
* [Enrich a company profile](/api-reference/enrich_profile/company) is free and never consumes credits.
* [Enrich a lead profile](/api-reference/enrich_profile/lead) costs 0.1 credit per profile found,
and nothing when no profile matches.
Catch-all emails are charged when catch-all verification is off. When it is on, only emails that
come back `catch_all_safe` are charged.
Every result payload carries `credits_consumed` for that request and `credits_left` for the account,
so you can reconcile without a second call.
## Checking your balance
```bash theme={null}
curl https://app.bettercontact.rocks/api/v2/account \
-H "X-API-Key: YOUR_API_KEY"
```
```json Response theme={null}
{
"success": true,
"credits_left": 32377,
"email": "you@yourcompany.com"
}
```
The balance is shared across your whole organisation, not per user and not per API key.
## Running out
There are two distinct behaviours, and they are easy to confuse.
The request is refused with [`402`](/api-reference/errors). Nothing is queued, nothing is
charged, and there is no `request_id` to poll. Top up, then submit again.
The request switches to the `on_hold` status. It is **not** lost: top up and it resumes on its
own. Do not resubmit, or you will pay twice for the leads already processed. See
[Request statuses](/api-reference/statuses).
When the balance covers only part of the page, the search returns as many companies as the
balance can pay for, and ends `terminated`. When it cannot pay for a single company, the search
ends `on_hold`, charges nothing, and does **not** resume after a top up: submit it again.
Submissions are not idempotent. A retried `POST` that had actually succeeded creates a second
request and consumes credits twice.
# Check your credits balance
Source: https://doc.bettercontact.rocks/api-reference/endpoint/account
GET /account
Read the credit balance of the account your API key belongs to.
The balance is shared across your whole organisation. See [Credits](/api-reference/credits) for
when a credit is actually consumed.
# Get a Company Finder Search results
Source: https://doc.bettercontact.rocks/api-reference/endpoint/company_finder_get
GET /company_finder/async/{request_id}
Get companies from a submitted Company Finder search.
**Poll on `status`, not on the HTTP status code.**
While the search is running this endpoint answers `202 Accepted` with a body that has no
`companies` and no `summary`. Once it is done it answers `200 OK` with `status` set to
`terminated` or `on_hold`.
Always branch on `body.status`, never on the HTTP code alone.
See [Request statuses](/api-reference/statuses) for the full lifecycle and a polling recipe.
## Reading the result
| `status` | HTTP | What you get |
| - | - | - |
| `not_started`, `processing` | `202` | `id`, `status`, `credits_left`, `credits_consumed`. Poll again. |
| `terminated` | `200` | `summary` and `companies`. When nothing matched, `companies` is `[]` and `summary.companies_found` is `0`. |
| `on_hold` | `200` | No results. The balance could not cover a single company. |
An `on_hold` Company Finder search does **not** resume after a top up. Top up, then submit the
search again. Nothing was charged for the first one.
Put a timeout on your polling loop. If the search provider fails, the request can stay in
`processing` instead of moving to a final status.
`credits_left` and `credits_consumed` are returned as decimal strings, for example `"998.5"`. Parse
them before doing arithmetic.
# New Company Finder Search
Source: https://doc.bettercontact.rocks/api-reference/endpoint/company_finder_post
POST /company_finder/async
Find companies that match firmographic, funding and hiring criteria.
Several filters only accept **exact** values from a fixed list: industries, technologies, revenue
ranges and funding round names. Look them up in [Taxonomies](/api-reference/taxonomies). A value
that is not in the list silently matches nothing.
Company Finder has **no webhook**. Retrieve the results by polling
[`GET /company_finder/async/{request_id}`](/api-reference/endpoint/company_finder_get).
## Filters
* List filters only take `include`. Unlike Lead Finder, there is no `exclude`: it is silently ignored.
* Unknown keys are silently ignored too, so check the spelling against the reference below. Lead
Finder keys such as `job_title` do not exist here.
* At least one filter must hold a real value, otherwise the request is refused with `400`.
* `company` is case-sensitive: send domains in lowercase.
## Pagination
`limit` and `offset` paginate a single search. `offset` is **zero-based**: keep `limit` constant and
step `offset` by `limit` on each call.
| Call | `limit` | `offset` | Companies |
| - | - | - | - |
| 1st | `100` | `0` (or omitted) | 1 to 100 |
| 2nd | `100` | `100` | 101 to 200 |
| 3rd | `100` | `200` | 201 to 300 |
The total number of matching companies is returned as `summary.companies_found`, so you know how many
pages to walk. Each page is a new search and is charged on its own, see [Credits](/api-reference/credits).
# Enrich leads
Source: https://doc.bettercontact.rocks/api-reference/endpoint/create
POST /async
Submit up to 100 leads for waterfall email and phone enrichment.
Provide `first_name`, `last_name` and either `company` or `company_domain`. `company_domain` gives
materially better results than a company name. Add `linkedin_url` whenever you have it, and always
when you request `enrich_phone_number`.
This endpoint is asynchronous. A `201` means the batch was accepted, not that it was enriched.
Keep the returned `id` and read [Request statuses](/api-reference/statuses) before you build your
polling loop.
Submissions are **not idempotent**: retrying a `POST` that had actually succeeded enriches the
same leads twice and consumes credits twice.
Anything you put in `custom_fields` comes back untouched in the results, which is the simplest way
to reconcile enriched leads with your own records.
# Get Enrichment Results
Source: https://doc.bettercontact.rocks/api-reference/endpoint/get
GET /async/{request_id}
Retrieve the enrichment results for a specific request ID.
**Poll on `status`, not on the HTTP status code.**
While the enrichment is running this endpoint answers `202 Accepted` with a body that has no `data`
and no `summary`. Once it is done it answers `200 OK` with `status: "terminated"`.
Some HTTP clients treat any `2xx` as success, so a naive integration reads an empty `data` and
concludes the lead could not be enriched. Always branch on `body.status === "terminated"`.
See [Request statuses](/api-reference/statuses) for the full lifecycle and a polling recipe.
Prefer the `webhook` attribute of [`POST /async`](/api-reference/endpoint/create) over polling.
Results are pushed as soon as they are ready, with no rate limit spent.
See [Webhooks](/api-reference/webhooks).
# Get a Lead Finder Search results
Source: https://doc.bettercontact.rocks/api-reference/endpoint/lead_finder_get
GET /lead_finder/async/{request_id}
Get leads from a submitted Lead Finder search.
**Poll on `status`, not on the HTTP status code.**
While the search is running this endpoint answers `202 Accepted` with a body that has no `leads`
and no `summary`. Once it is done it answers `200 OK` with `status` set to `terminated`,
`not_found` or `on_hold`.
Always branch on `body.status`, never on the HTTP code alone.
See [Request statuses](/api-reference/statuses) for the full lifecycle and a polling recipe.
Prefer the `webhook` attribute of [`POST /lead_finder/async`](/api-reference/endpoint/lead_finder_post)
over polling. See [Webhooks](/api-reference/webhooks).
# New Lead Finder Search
Source: https://doc.bettercontact.rocks/api-reference/endpoint/lead_finder_post
POST /lead_finder/async
Find new leads based on Company and People criteria.
Several filters only accept **exact** values from a fixed list: industries, technologies,
seniority levels, departments, functions, revenue ranges, funding round names and countries. Look
them up in [Taxonomies](/api-reference/taxonomies). A value that is not in the list silently
matches nothing.
`exclude` is applied after the search runs, so a request built only from `exclude` values returns
nothing. Always pair it with at least one `include`.
## Pagination
`limit` and `offset` paginate a single search. `offset` is **zero-based**: keep `limit` constant and
step `offset` by `limit` on each call.
| Call | `limit` | `offset` | Leads |
| - | - | - | - |
| 1st | `100` | `0` (or omitted) | 1 to 100 |
| 2nd | `100` | `100` | 101 to 200 |
| 3rd | `100` | `200` | 201 to 300 |
The total number of matching leads is returned as `summary.leads_found`, so you know how many pages
to walk. If you do not need pagination, use `max_leads` instead and ignore `limit` and `offset`.
# Enrich a company profile
Source: https://doc.bettercontact.rocks/api-reference/enrich_profile/company
POST /enrich_profile/company
Look up a single company by domain or LinkedIn URL.
**This endpoint is synchronous and does not consume credits.**
Unlike the enrichment and Lead Finder APIs, there is no `request_id` and nothing to poll: the
company profile comes back in the same response.
Send `company_domain`, `company_linkedin_url`, or both. At least one is required, otherwise the call
is rejected with `422`.
```bash cURL theme={null}
curl -X POST https://app.bettercontact.rocks/api/v2/enrich_profile/company \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "company_domain": "microsoft.com" }'
```
```javascript Node.js theme={null}
const res = await fetch(
"https://app.bettercontact.rocks/api/v2/enrich_profile/company",
{
method: "POST",
headers: {
"X-API-Key": process.env.BETTERCONTACT_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({ company_domain: "microsoft.com" }),
}
);
const { success, data } = await res.json();
```
```python Python theme={null}
import os, requests
res = requests.post(
"https://app.bettercontact.rocks/api/v2/enrich_profile/company",
headers={"X-API-Key": os.environ["BETTERCONTACT_API_KEY"]},
json={"company_domain": "microsoft.com"},
)
company = res.json()["data"]
```
## Authentication
This endpoint accepts the key **either** in the `X-API-Key` header, like every other endpoint, **or**
as an `api_key` attribute in the body. The header is the recommended form: it keeps the key out of
your request payloads and out of your logs.
This is also the endpoint to use when a Lead Finder search turns out to be a single company
lookup: that case is rejected with a `422`. See [Errors](/api-reference/errors).
## What you get back
The response mirrors the `company_*` block of the [Lead Finder](/api-reference/endpoint/lead_finder_get)
payload, so the same parsing code works on both.
A few fields deserve a note:
| Field | Note |
| - | - |
| `company_domain` / `company_website` | Always the same value. |
| `company_type` | Normalised: lowercased, spaces replaced by underscores. `Privately Held` comes back as `privately_held`. |
| `company_employees_range_start` / `_end` | Parsed from the size range. `11-50` gives `11` and `50`. An open ended range such as `10000+` sets the start only and leaves the end `null`. |
| `company_head_quarters_city` and the `company_address_*` fields | May be an **empty string** rather than `null` when the source has no value. Test for emptiness, not just for `null`. |
| `company_keywords` | An array of strings, not a comma separated string. |
Every other `company_*` key from the Lead Finder payload is present in the response and always
`null`. They are kept for payload compatibility, so do not read anything into them.
Landbase specific attributes such as `revenue`, `is_b2b` and `technologies_used` are **not**
returned by this endpoint. You can still filter on them through
[Lead Finder](/api-reference/endpoint/lead_finder_post).
# Enrich a lead profile
Source: https://doc.bettercontact.rocks/api-reference/enrich_profile/lead
POST /enrich_profile/lead
Look up a single person and get their profile and current company.
**This endpoint is synchronous. It costs 0.1 credit per profile found**, and nothing when no
profile matches.
There is no `request_id` and nothing to poll: the profile comes back in the same response. Expect
a couple of seconds.
This endpoint returns **profile data only**. It never returns an email address or a phone number.
For contact data, use [Enrich leads](/api-reference/endpoint/create).
## Identifying the lead
There are two ways in, and they are not equivalent.
Send `linkedin_url` alone. This is the most reliable form and the one to prefer whenever you have
the URL.
Send `first_name`, `last_name` and `company_domain` together. All three are required.
**`company_domain` is required in the second form, a company name is not enough.** Sending
`first_name` + `last_name` + `company` without `company_domain` is rejected with `422`. You can
send `company` in addition to `company_domain`, but never instead of it.
When `linkedin_url` is present the other attributes are ignored.
```bash cURL theme={null}
curl -X POST https://app.bettercontact.rocks/api/v2/enrich_profile/lead \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "linkedin_url": "https://www.linkedin.com/in/veronick-martinuzzi-a4533373" }'
```
```javascript Node.js theme={null}
const res = await fetch(
"https://app.bettercontact.rocks/api/v2/enrich_profile/lead",
{
method: "POST",
headers: {
"X-API-Key": process.env.BETTERCONTACT_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
linkedin_url: "https://www.linkedin.com/in/veronick-martinuzzi-a4533373",
}),
}
);
const { success, data } = await res.json();
if (!success) throw new Error("Lookup failed");
```
```python Python theme={null}
import os, requests
res = requests.post(
"https://app.bettercontact.rocks/api/v2/enrich_profile/lead",
headers={"X-API-Key": os.environ["BETTERCONTACT_API_KEY"]},
json={"first_name": "Veronick", "last_name": "Martinuzzi", "company_domain": "hondaresearch.com"},
)
body = res.json()
if not body["success"]:
raise RuntimeError(body["error"])
profile = body["data"]
```
## Authentication
The key goes **either** in the `X-API-Key` header, like every other endpoint, **or** as an `api_key`
attribute in the body. The header is the recommended form: it keeps the key out of your request
payloads and out of your logs.
## What you get back
25 fields describing the person and their current company. Unlike the enrichment and Lead Finder
payloads, there are **no extra always-null legacy keys**: what you see is the whole response.
A few fields deserve a note:
| Field | Note |
| - | - |
| `contact_linkedin_headline` | Often an **empty string** rather than `null` when the profile has no headline. |
| `contact_location_continent` / `_city` | Frequently `null` even when country and state are filled. Do not assume the location block is all-or-nothing. |
| `company_headquarters_city` | Can carry a full street address rather than a bare city name. |
| `company_employees_range_end` | `null` on open ended ranges: a company in `10001+` has a start and no end. |
| `company_keywords` | An array of strings, not a comma separated string. |
| `company_type`, `company_founded_year` | Frequently `null`. |
Values are capitalised on the way out, which is why a job title comes back as
`Development solutions sr. principal engineer` rather than in its original casing.
# Errors
Source: https://doc.bettercontact.rocks/api-reference/errors
Every status code the API can return, and what to do about it
## Error shape
Errors are returned as JSON. The human readable text sits either in `error` or in `message`
depending on the endpoint, so read both.
```json theme={null}
{
"success": false,
"error": "Bad request. Please check documentation"
}
```
`2xx` does not mean the results are ready. The `GET` endpoints answer `202` while a request is
still running. See [Request statuses](/api-reference/statuses).
## Status codes
| Code | Meaning | What to do |
| - | - | - |
| `200` | Request finished. Check `status` in the body before using the results. | Read `status`. |
| `201` | Enrichment accepted. | Keep the returned `id`. |
| `202` | Accepted, or still running on the `GET` endpoints. | Poll again, or wait for the webhook. |
| `400` | Malformed request. | Fix the payload, do not retry as is. |
| `401` | API key missing, invalid or deactivated. | Check the `X-API-Key` header. |
| `402` | Out of credits, or the feature is not on your plan. | Read `message` to tell the two apart. |
| `404` | Unknown Lead Finder or Company Finder `request_id`, or nothing matched on an [Enrich Profile](/api-reference/enrich_profile/lead) lookup. | Check the input, do not retry. |
| `406` | Unknown enrichment `request_id`. | Check the id, do not retry. |
| `422` | The request could not be accepted. | Read `message`, do not retry as is. |
| `429` | [Rate limit](/api-reference/api_rate_limits) exceeded. | Back off and retry. |
| `500` | Unexpected error on our side. | Safe to retry with backoff. |
`404` and `406` both mean "unknown request id", but they are **not interchangeable**: the
enrichment endpoint answers `406`, the Lead Finder and Company Finder endpoints answer `404`. Handle both if your
client talks to the two APIs.
## Messages you are likely to hit
### 400 Bad request
| Message | Cause |
| - | - |
| `Bad request. Please check documentation` | `data` is missing, or it is not an array. |
| `No more than 100 contacts allowed in a single batch call` | The `data` array holds more than 100 leads. Split it. |
| `Filters parameter is required.` | The Lead Finder request has no `filters` object. |
| `Filters parameter is invalid. Include and Exclude can not be blank` | Every filter is empty. At least one value is required. |
| `Filters parameter is required.` | The Company Finder request has no `filters` object, or an empty one. |
| `Filters parameter is invalid. Include values can not be blank` | Every Company Finder filter is empty. At least one value is required. |
### 401 Unauthorized
| Message | Cause |
| - | - |
| `Your are not authorized. Please check your api_key.` | The key is missing, wrong, or was deactivated. |
Find your key in [your API settings](https://app.bettercontact.rocks/api_requests). It travels in
the `X-API-Key` header, not in an `Authorization: Bearer` header.
### 402 Payment required
| Message | Cause |
| - | - |
| `You ran out of credits` | The account balance is empty. |
| `You do not have enough credits to perform a profile search.` | Not enough credits for an [Enrich a lead profile](/api-reference/enrich_profile/lead) lookup. |
| `You do not have enough tokens to use Lead Finder API.` | Same, on the Lead Finder API. |
| `You do not have enough tokens to use Company Finder API.` | Same, on the Company Finder API. |
| `You don't have access to phone number enrichment feature. Please upgrade your plan.` | `enrich_phone_number` is `true` without the phone add-on. |
| `You don't have access to verify catch_all feature. Please upgrade your plan.` | `verify_catch_all` is `true` without the catch-all add-on. |
A `402` on submission is different from the `on_hold` status. `402` means the request was refused.
`on_hold` means an accepted request paused mid-flight because the balance hit zero. Top up and it
resumes on its own. See [Credits](/api-reference/credits).
### 422 Unprocessable
| Message | Cause |
| - | - |
| `You are out of free lead finder requests. Please upgrade your plan to continue using this feature.` | The free search quota is exhausted. |
| `You're looking to enrich a company, please use dedicated endpoint: ...` | The search targets a single company domain with `limit: 1` and no other filter. That is a company lookup, not a search: use [Enrich a company profile](/api-reference/enrich_profile/company) instead. |
| `Lead finder was not accepted.` | The request failed validation, most often `limit`, `offset` or `max_leads` out of bounds. |
| `You are out of free company finder requests. Please upgrade your plan to continue using this feature.` | The free search quota is exhausted. |
| `Company finder was not accepted.` | `limit` is above 200. |
| `company_domain or company_linkedin_url is required.` | [Enrich a company profile](/api-reference/enrich_profile/company) was called without any identifier. |
| `Linkedin url OR First_name, last_name, company and company domain is missing` | [Enrich a lead profile](/api-reference/enrich_profile/lead) was called without `linkedin_url` and without the full `first_name` + `last_name` + `company_domain` trio. |
## Retry policy
Retry `429` and `500` with exponential backoff. Never retry `400`, `401`, `404`, `406` or `422`
unchanged: the same request will fail the same way. For `402`, top up first.
Submissions are **not idempotent**. Retrying a `POST` that actually succeeded creates a second
request and consumes credits twice. When a `POST` times out with no response body, poll
[`GET /account`](/api-reference/endpoint/account) or check your requests in the app before
resubmitting.
# Request statuses
Source: https://doc.bettercontact.rocks/api-reference/statuses
How to know when an async request is really done
The enrichment API, the Lead Finder API and the Company Finder API are asynchronous. You submit a request, you get a
`request_id` back immediately, and the work happens in the background.
**Always branch on `status`, never on the HTTP status code alone.**
While a request is running, the `GET` endpoints answer `202 Accepted` with a body that has **no
`data`** (enrichment), **no `leads`** (Lead Finder) and **no `companies`** (Company Finder). Many HTTP clients treat every `2xx` as
success, so an integration that only checks `response.ok` reads an empty result and wrongly
concludes that nothing was found.
## Lifecycle
`POST /async` answers `201`, `POST /lead_finder/async` and `POST /company_finder/async` answer
`202`. The body carries the identifier you will poll on: `id` for enrichment, `request_id` for
Lead Finder and Company Finder.
The `GET` endpoint answers `202` with `status: "processing"` and a `message`. No `data`, no
`leads`, no `summary`.
The `GET` endpoint answers `200` with `status: "terminated"`. `summary` and the results array
are now present.
## Status values
| `status` | HTTP | Meaning |
| - | - | - |
| `not_started` | `202` | Accepted, queued, not picked up yet. |
| `processing` | `202` | Running. Poll again later. |
| `on_hold` | `200` | Paused because the account ran out of credits. Top up and the request resumes on its own, no need to resubmit. **Company Finder is the exception:** see below. |
| `terminated` | `200` | Finished. This is the only status that guarantees results are present. |
A Lead Finder search that matched nothing is reported as `terminated` with an empty `leads` array
and `summary.leads_found` at `0`. A Company Finder search that matched nothing is reported the same
way, with an empty `companies` array and `summary.companies_found` at `0`. It is a normal outcome,
not an error.
A Company Finder search that ends `on_hold` does **not** resume after a top up. Nothing was charged:
top up, then submit the search again.
## Polling recipe
Prefer a [webhook](/api-reference/webhooks) whenever you can. When you do have to poll, wait a few
seconds between attempts and stop on `terminated`, keeping an eye on the
[rate limit](/api-reference/api_rate_limits).
```javascript Node.js theme={null}
async function waitForResults(requestId, apiKey, { timeoutMs = 300000 } = {}) {
const deadline = Date.now() + timeoutMs;
while (Date.now() < deadline) {
const res = await fetch(
`https://app.bettercontact.rocks/api/v2/async/${requestId}`,
{ headers: { "X-API-Key": apiKey } }
);
if (res.status === 401) throw new Error("Invalid API key");
if (res.status === 406) throw new Error("Unknown request_id");
const body = await res.json();
// Branch on `status`, not on `res.ok`.
if (body.status === "terminated") return body;
if (body.status === "on_hold") throw new Error("Out of credits, top up to resume");
await new Promise((r) => setTimeout(r, 5000));
}
throw new Error("Timed out waiting for results");
}
```
```python Python theme={null}
import time
import requests
def wait_for_results(request_id, api_key, timeout=300):
deadline = time.time() + timeout
while time.time() < deadline:
res = requests.get(
f"https://app.bettercontact.rocks/api/v2/async/{request_id}",
headers={"X-API-Key": api_key},
)
if res.status_code == 401:
raise RuntimeError("Invalid API key")
if res.status_code == 406:
raise RuntimeError("Unknown request_id")
body = res.json()
# Branch on `status`, not on the HTTP code.
if body["status"] == "terminated":
return body
if body["status"] == "on_hold":
raise RuntimeError("Out of credits, top up to resume")
time.sleep(5)
raise TimeoutError("Timed out waiting for results")
```
For Lead Finder, use `GET /lead_finder/async/{request_id}` and treat `404` as the unknown-id case
instead of `406`. For Company Finder, use `GET /company_finder/async/{request_id}`, also with `404`,
and treat `on_hold` as final: it never resumes on its own.
## Per lead status
Inside a terminated enrichment, each row of `data` carries its own `enriched` boolean. A batch can
be `terminated` while some of its leads could not be enriched. Check `enriched` before using a row.
# Taxonomies
Source: https://doc.bettercontact.rocks/api-reference/taxonomies
Reference values for Lead Finder filter parameters
When sending values to the [Lead Finder](/api-reference/endpoint/lead_finder_post) endpoint, several filters require **exact** values from a predefined list. Use the references below to look up accepted values.
Lookup is case-sensitive — `"Software Development"` and `"software development"` are not equivalent.
These taxonomies are derived from the Landbase data provider. They may evolve over time — bookmark this page.
## Industries
Used by `company_industry`. **120 values.**
```
Accounting
Airlines/Aviation
Alternative Medicine
Animation
Apparel & Fashion
Architecture & Planning
Automotive
Aviation & Aerospace
Banking
Biotechnology
Broadcast Media
Building Materials
Business Supplies & Equipment
Capital Markets
Chemicals
Civic & Social Organization
Civil Engineering
Commercial Real Estate
Computer & Network Security
Computer Games
Computer Hardware
Computer Networking
Computer Software
Construction
Consumer Electronics
Consumer Goods
Consumer Services
Cosmetics
Defense & Space
Design
E-Learning
Education Management
Electrical & Electronic Manufacturing
Entertainment
Environmental Services
Events Services
Executive Office
Facilities Services
Financial Services
Fine Art
Food & Beverages
Food Production
Gambling & Casinos
Government Administration
Government Relations
Graphic Design
Health, Wellness & Fitness
Higher Education
Hospital & Health Care
Hospitality
Human Resources
Individual & Family Services
Industrial Automation
Information Services
Information Technology & Services
Insurance
International Affairs
International Trade & Development
Internet
Investment Banking
Investment Management
Law Enforcement
Law Practice
Legal Services
Leisure, Travel & Tourism
Logistics & Supply Chain
Luxury Goods & Jewelry
Management Consulting
Maritime
Market Research
Marketing & Advertising
Mechanical or Industrial Engineering
Media Production
Medical Device
Medical Practice
Mental Health Care
Military
Mining & Metals
Motion Pictures & Film
Music
Nanotechnology
Non-profit Organization Management
Oil & Energy
Online Media
Outsourcing/Offshoring
Package/Freight Delivery
Performing Arts
Pharmaceuticals
Philanthropy
Photography
Political Organization
Primary/Secondary Education
Professional Training & Coaching
Program Development
Public Policy
Public Relations & Communications
Publishing
Real Estate
Religious Institutions
Renewables & Environment
Research
Restaurants
Retail
Security & Investigations
Semiconductors
Shipbuilding
Software Development
Sports
Staffing & Recruiting
Telecommunications
Think Tanks
Translation & Localization
Transportation/Trucking/Railroad
Utilities
Venture Capital & Private Equity
Veterinary
Wholesale
Wine & Spirits
Wireless
Writing & Editing
```
## Departments
Used by `lead_department`. **27 values.**
* `Engineering`
* `Software Development`
* `Sales`
* `Marketing`
* `Finance`
* `Operations`
* `Human Resources`
* `Product Management`
* `Design`
* `Customer Success`
* `Legal`
* `Information Technology`
* `Research & Development`
* `Business Development`
* `Communications`
* `Data & Analytics`
* `Consulting`
* `Healthcare`
* `Education`
* `Administrative`
* `Accounting`
* `Strategy`
* `Procurement`
* `Quality Assurance`
* `Recruiting`
* `Public Relations`
* `Media & Content`
## Functions
Used by `lead_function`. Functions accept the **same values as Departments above** — both filter on the same underlying field (Landbase `member_departments`). If you set both `lead_department` and `lead_function` in the same request, the values are merged.
## Seniority levels
Used by `lead_seniority`. **12 values.**
* `c_suite`
* `vp`
* `director`
* `head`
* `manager`
* `senior`
* `mid-level`
* `entry`
* `founder`
* `owner`
* `partner`
* `intern`
## Company size ranges
Listed for reference. **9 values.**
You don't pass these strings directly via the API — instead, set `company_headcount_min` and/or `company_headcount_max` (integers), and the request is converted server-side into overlapping size ranges from this list.
* `0-1`
* `1-10`
* `11-50`
* `51-200`
* `201-500`
* `501-1000`
* `1001-5000`
* `5001-10000`
* `10001+`
## Revenue ranges
Used by `revenue_ranges`. **9 values.**
* `$0-$1M`
* `$1M-$5M`
* `$5M-$20M`
* `$20M-$100M`
* `$100M-$500M`
* `$500M-$1B`
* `$1B-$5B`
* `$5B-$10B`
* `$10B+`
## Funding round names
Used by `last_funding_round_names`. **29 values.**
* `Angel Round`
* `Pre Seed Round`
* `Seed Round`
* `Series A`
* `Series B`
* `Series C`
* `Series D`
* `Series E`
* `Series F`
* `Series G`
* `Series H`
* `Series I`
* `Series J`
* `Convertible Note`
* `Corporate Round`
* `Venture Round`
* `Grant`
* `Private Equity Round`
* `Post-IPO Equity`
* `Post-IPO Debt`
* `Post-IPO Secondary`
* `Equity Crowdfunding`
* `Product Crowdfunding`
* `Initial Coin Offering`
* `Debt Financing`
* `Non Equity Assistance`
* `Secondary Market`
* `Funding Round`
## Technologies
Used by `company_technologies`. **1757 values.**
```
amp
well
microsoft
impact
c
linkedin
facebook
python
adobe
ups
javascript
instagram
html
react
java
linux
medium
git
box
salesforce
autocad
mix
aos
mysql
docker
youtube
php
wordpress
chef
google analytics
spring
twitter
angular
dell
electric
kubernetes
segment
visa
vmware
recognize
postgresql
google ads
tiktok
github
canva
ease
sprint
figma
jenkins
zoom
mongodb
upkeep
jquery
prototype
quickbooks
terraform
swift
operative
asp.net
square
apache
json
deputy
sage
solidworks
bootstrap
google cloud
gmail
ansible
nodejs
selenium
nosql
unix
workday
signal
postman
shopify
slack
iam
redis
laravel
unity
charter
grafana
influential
django
redux
adp
rds
elk
servicenow
vue.js
ecc
sketch
graphql
windows 10
market leader
netsuite
informatica
google workspace
google sheets
flutter
hunter
elasticsearch
snowflake
hibernate
matlab
trello
asana
autodesk
primer
mailchimp
prometheus
hcm
carta
indeed
looker
angularjs
junit
rabbitmq
fortinet
reply
ubuntu
skype
splunk
veeam
databricks
gauges
zendesk
sketchup
namely
eclipse
onedrive
nginx
semrush
arcgis
j2ee
oracle database
xero
notion
greenhouse
fedex
cucumber
revue
magento
stats
zoho
webpack
google drive
db2
drupal
lever
google tag manager
chrome
sqs
centos
anchor
red hat
zabbix
google docs
qlik
sns
juniper
azure data factory
puppet
windows 7
tailwind
azure sql
workable
miro
drift
sonarqube
azure active directory
thematic
docusign
keynote
harvest
aws lambda
ruby on rails
scikit-learn
datadog
cognos
recharge
symfony
smartsheet
mobilize
jazzhr
catia
compass
nagios
okta
juris
blender
appium
martech
odoo
avaya
testng
mac os
hootsuite
successfactors
bluebeam
jboss
solarwinds
constant contact
mariadb
network solutions
sap hana
ecm
symantec
final cut pro
qlikview
sales navigator
monday.com
stripe
ansys
mulesoft
sophos
bamboo
netapp
wireshark
qgis
moodle
openai
intercom
cobol
woocommerce
avere
kronos
uipath
seismic
alteryx
talend
debian
ahrefs
snapchat
4d
labview
gravity
usps
mastercard
android studio
mockito
soapui
dynatrace
altium
esri
verizon
delphi
telegram
paypal
q4
xcode
marketo
pwc
simulink
pardot
unreal engine
dropbox
google data studio
nielsen
user interviews
sqlite
viewpoint
stata
storybook
veritas
cerner
kms
discord
oracle ebs
azure databricks
xamarin
g suite
zoominfo
amplitude
windows server 2012
clickup
mcafee
entrata
airtable
invision
cisco asa
wix
azure data lake
circleci
joomla
google maps
mikrotik
cisco meraki
cisco routers
teradata
openstack
infinity
nunit
sap business one
zapier
windows xp
new relic
crowdstrike
webflow
nutanix
cisco switches
sap fico
klaviyo
sprout
qualtrics
welcome to the
calendly
sentry
testrail
windows server 2008
cyberark
emerson
google my business
zephyr
google meet
sonicwall
webs
oracle pl/sql
putty
logstash
crisp
pipedrive
nvidia
bing ads
teamviewer
ey
camtasia
prestashop
xerox
zebra
tibco
sap ariba
sap hcm
jcl
upwork
cloudflare
hubspot crm
robot framework
sitecore
hbase
commvault
vimeo
hotjar
buildout
emma
cloudera
ethereum
reddit
tekla
podium
at&t
concord
articulate storyline
anaplan
kaspersky
discourse
transcend
hashicorp
jira software
zscaler
paylocity
rd station
aws cloudformation
screaming frog
coreldraw
publica
brocade
the org
sodexo
squarespace
widen
freshdesk
google bigquery
sap concur
control-m
amazon s3
myob
sap analytics cloud
presidio
azure monitor
oracle forms
perfecto
appdynamics
twilio
basecamp
agilent
ivanti
wonderware
nuance
udemy
google calendar
knockout
ibm websphere
braze
meditech
uikit
f24
infoblox
mixpanel
sap business objects
lumion
mitel
acronis
windows server 2003
cegid
ibm mq
haproxy
trend micro
hugo
paycom
needles
tableau server
camunda
polling
browserstack
webrtc
cisco aci
sap crm
ceridian
taleo
proofpoint
vero
pvsyst
alexa
westlaw
clutch
blackbaud
ultipro
cvent
amazon redshift
cpanel
katalon
bamboohr
3cx
telerik
salesloft
heartland
oracle cloud infrastructure
citrix xenapp
trustpilot
affirm
toast
loom
optimizely
jira service management
lexisnexis
sap master data
watchguard
liquibase
tableau desktop
cashbook
google classroom
auth0
bugzilla
umbraco
elastic stack
contentful
redcap
jira service desk
fiddler
fiserv
materialize
aon
factorytalk
duda
cisco webex
clef
typo3
google forms
oracle 10g
onetrust
dojo
hikvision
anydesk
openvpn
mckesson
sendgrid
vagrant
ecmascript
memcached
trac
amazon ec2
oracle 12c
oracle linux
hugging face
bullhorn
winscp
azure sentinel
sourcetree
inflight
vercel
fiverr
sap netweaver
amazon advertising
clio
avalara
bigcommerce
azure stack
rubrik
keyshot
whatsapp business
sentinelone
ibm mainframe
oracle epm
apple ios
sap cloud platform
trivia
saltstack
eventbrite
zbrush
perkbox
socket.io
veracode
audacity
revit mep
qtest
posh
oracle business intelligence
webmethods
planview
cisco unified communications
braintree
do you dream
ab initio
webtrends
optum
hp-ux
cisco voip
styled-components
criteo
iqvia
three.js
atlassian confluence
five9
citrix netscaler
cakephp
msci
national instruments
aol
oracle soa
sumo
kyriba
sparked
android os
qnap
meltwater
ibm i
surveymonkey
bitbucket pipelines
amazon connect
mapbox
open systems
store manager for
ukg pro
oracle hcm cloud
zillow
cisco anyconnect
quora
apple pay
lusha
imovie
netskope
tealium
typeform
palantir
activecampaign
costar
imperva
quickbase
dentrix
hyperledger
mobileiron
tinyurl
openssl
vitally
temenos
highcharts
termly
sap data services
snagit
cision
firebug
tableau software
cisco jabber
kickstarter
taboola
comscore
matomo
google kubernetes engine
ekahau
ibm aix
livechat
aws step functions
forescout
flock
cisco umbrella
avid media composer
oracle peoplesoft
deltek costpoint
ibm sterling
appfolio
flywheel
charles proxy
irene
sap plant maintenance
gimp
zerto
ibm tivoli
oracle fusion middleware
onpage
hazelcast
pendo
lucanet
sap supply chain
algolia
korn ferry
google marketing platform
prezi
classy
madcap flare
kudos
epic software
unruly
aws iot
plesk
testflight
sendinblue
backbone.js
eclinicalworks
saviynt
arcserve
bloomberg terminal
amazon business
keyword discovery
opentable
citrix virtual apps
the trade desk
amazon eks
jobstreet
pecos
medidata
mule esb
ning
litmus
netlify
lectora
encase
sap business warehouse
jotform
tumblr
rsa archer
apollo.io
privy
helioscope
canvas lms
klarna
usertesting
bitrix24
freshworks
campaign monitor
salsify
imagej
sap is-u
planful
samsara
phpmyadmin
gorgias
playable
crunchbase
aws fargate
apttus
outbrain
alibaba cloud
visual studio team
chime
coinbase
amazon dynamodb
google firebase
walkme
sap integration suite
realpage
sharefile
google sites
archibus
supersonic
highspot
ibm filenet
houzz
redgate
sencha
kaggle
amazon ecs
unbounce
governmentjobs
plaid
oracle application express
oracle retail
transunion
factorial
razorpay
grass valley
quip
amazon emr
sustainalytics
weebly
bloomreach
akeneo
cloverleaf
on24
dun & bradstreet
mobaxterm
elastic cloud
moengage
mastercontrol
demandware
pantheon
sap crystal reports
oracle crm
tenfold
clarity ppm
fullstory
teamtailor
responsys
mailjet
aws security hub
nexthink
applied epic
everbridge
google groups
lodash
onelogin
sap successfactors employee
inkscape
fishbowl
windows vista
muut
citrix adc
prebid
mindbody
aws backup
oracle cpq
arkit
gotowebinar
ipro
oracle utilities
sps commerce
amazon cloudwatch
it glue
infusionsoft
egnyte
xactly
cisco ironport
kajabi
amazon athena
siteimprove
atlassian cloud
adform
buzzfeed
quarkxpress
squish
genially
jinja2
carto
clickfunnels
oracle siebel
google adsense
cisco collaboration
formstack
peoplenet
aws secrets manager
dealertrack
google app engine
authorize.net
oracle solaris
hover
openstreetmap
sap controlling
yext
customer.io
pimcore
lifeworks
linktree
azure application gateway
capterra
mozilla firefox
diverse solutions
eaglesoft
chargebee
accela
aws identity and
athenahealth
oracle eloqua
yola
google dialogflow
syndigo
donorperfect
arcore
connectwise automate
kaltura
storyblok
beamer
teamworks
pipl
ibwave
datatables
amazon api gateway
buxton
ams360
procare
liveramp
lightbox
yotpo
google hangouts
piwik
wordperfect
sap extended warehouse
panopto
efront
windows server 2000
sphera
resy
callmanager
mailerlite
bluejeans
vidyard
culture amp
patreon
cornerstone ondemand
emberjs
requirejs
onesignal
bynder
nice incontact
aws direct connect
azure load balancer
rsa securid
coveo
mailup
everyaction
userzoom
realtor.com
adroll
emaint
my hours
retail pro
motus
mailgun
techsmith
tock
wetransfer
perma
sap business planning
loopnet
pearson vue
bazaarvoice
ableton live
edirectory
bmc remedy itsm
sap financial accounting
google ai
cloudinary
oracle cloud services
instructure
filevine
recaptcha
gensuite
skai
securecrt
amazon elastic kubernetes
dalet
d3js
foursquare
zotero
model n
mediaocean
oracle hyperion planning
sap sales cloud
hubspot marketing hub
time matters
libris
wpbakery
afterpay
oracle financials cloud
newtek
prometric
freshsales
mavenlink
honeypot
alchemer
aws route53
gitkraken
loftware
slido
wistia
amazon vpc
homecare homebase
doubleverify
oracle brm
atlassian bitbucket
muck rack
mri software
ultimate kronos group
dynamic yield
brightsign
reynolds and reynolds
stoplight
sharpspring
kofax capture
oracle human capital
adra
paychex flex
duo security
vevo
oracle financial services
lcn
bugsnag
swirl
rollbar
trulia
evernote
benevity
akamai cdn
oracle commerce
hotdocs
dealcloud
brainshark
qualia
sap hybris commerce
cinc
automox
amazon cloudfront
azure traffic manager
aws x-ray
crazy egg
mytime
crossmatch
google charts
appnexus
amazon mq
yardi systems
wp engine
haml
biba
genesys purecloud
tradingview
freshchat
time doctor
abb robotics
care.com
google translate
revinate
pricefx
conviva
garageband
convertkit
cosential
creditsafe
searchmetrics
oracle aconex
flashtalking
mouseflow
databox
flurry
swaggerhub
angie's list
trifacta
ccrm
movable ink
shogun
gofundme
openssh
strato
amcharts
rocketreach
mandrill
bright horizons
ibm watson health
oracle demantra
intershop
first advantage
icontact
gigya
victoria metrics
hubstaff
crashplan
callrail
stackadapt
mendeley
wpml
gleam
sap logistics execution
ibm maas360
demandtools
xray test management
toggl
talentlms
sap enterprise asset
volgistics
coupa procurement
logrocket
4me
sap commissions
ethicspoint
bmc patrol
granicus
ringlead
boku
pubmatic
engagement hub
omnibees
aws key management
juicer
goodtime
help scout
informacast
shareasale
hostinger
silverstripe
trustwave
drata
quizizz
bizible
emc documentum
zywave
oracle hospitality
openresty
ollydbg
azure cdn
amazon ses
gtreasury
h&r block
onfido
pclaw
sap customer data
daptiv
sapling
citrix sharefile
google authenticator
sendoso
skyslope
amazon relational database
wufoo
accruent
vistaprint
people per hour
sap sybase
etools
amplience
google wallet
orientdb
brightwheel
emply
coralogix
handlebars.js
iris software
serrala
smartlook
hostgator
comit
blueshift
aweber
aws elastic load
evie
smarsh
listrak
amocrm
fl studio
oracle warehouse management
salesmanago
givesmart
channeladvisor
canny
modern hire
ioffice
veris
apollo global management
connectwise control
ibm watson assistant
1link
crimson hexagon
infogram
ibm cics
quentic
reactivex
clinc
snovio
opentsdb
progress software
sandler training
respondus
citrix xenmobile
underscore.js
azure devops projects
handbrake
elastic kibana
birdeye
digg
contently
toptal
hubspot academy
tinymce
clearcare
soapbox
patch my pc
volusion
restaurant365
oracle fusion procurement
destini
ankorstore
zocdoc
blueconic
jstor
money guide pro
accelo
klipfolio
ow.ly
mixmax
ckeditor
openclinica
servicechannel
phenom people
amazon simple queue
oracle project management
censhare
branch metrics
font awesome
datawatch
nationbuilder
exterro
moment.js
dealersocket
lexis advance
email format
clearscope
docsend
integral ad science
guestline
flippingbook
stardog
sourceforge
inmoment
mootools
istock
justgiving
maxon cinema 4d
trizetto facets
jimdo
ibm spss statistics
gmass
siteground
leadfeeder
sendpulse
support hero
frontline education
podbean
wantedly
hansoft
oracle marketing cloud
business wire
spark hire
google admob
lucky orange
inszoom
pubnub
hockeyapp
codepen
bioclinica
libsyn
jobstart
amazon simple notification
wood mackenzie
nice systems
winautomation
appriver
leanplum
acuity scheduling
haptik
scrivener
tamr
ustream
palo alto next-generation
hi5
ecwid
livongo
amazon rekognition
nortel meridian
serpstat
flexera one
tawk.to
etracker
google doubleclick
price manager
masterpass
moveworks
oracle9i
calyx point
influitive
gandi
bugherd
ariba commerce
tapclicks
cleverreach
pulsepoint
zao
fullcalendar
simpli.fi
oracle business process
monsido
jw player
perfect audience
virtuemart
smugmug
alyce
actimize aml
formsite
civicplus
riskified
contact form 7
ketch
zemanta
olark
optinmonster
partnerstack
sap manufacturing integration
thryv
tidio
yubico
signifyd
sap credit management
liveagent
scribd
varnish cache
truste
urban airship
agile crm
simplepractice
boarddocs
sas data management
oracle master data
mopub
debounce
google remarketing
ibm kenexa
unsplash
visual website optimizer
zopim
vertex cloud
iadvize
protonmail
orion advisor
firestorm
eightfold.ai
socure
powr
paymentus
campbrain
cisco prime collaboration
donorbox
trusted insight
empower retirement
instrumentl
attendance on demand
trialworks
pandas python
sap erp central
datadome
boxever
wordfence
sociabble
wp rocket
gulpjs
flipsnack
netvibes
selectminds
io technologies
zylo
digipass
revcontent
calabrio one
convercent
amazon opensearch service
taxdome
teamsnap
maestroqa
speedcurve
quantros
rightcapital
dreamhost
cisco iot
gumroad
sap predictive analytics
lawpay
maxmind
oracle endeca
mozy
upslide
campminder
select2
signupgenius
appsumo
vendr
kentico xperience
limeade
kinsta
citrix workspace app
dreamdata
hubspot forms
ibm z series
teespring
breezy hr
pushpay
casetext
reviews.io
eastnets
id.me
dragon naturallyspeaking
ooyala
media.net
google sign-in
herefish
prophix software
setmore
marchex
feedback company
swrve
bentobox
cadence design systems
influxdata
rapidapi
sojern
amazon payments
vbulletin
iubenda
refersion
clicky
vade secure
synthetix
mailpoet
amicus attorney
kyruus
quorum software
roistat
pexels
helpjuice
oracle data warehousing
nasdaq nordic
polyfill
next jump
ingeniux
pros pricing
pricespider
demandforce
lendingclub
everyonesocial
modernizr
amazon machine learning
theplatform
thinglink
oracle knowledge management
choozle
fiberlink
cisive
jobsoid
imgur
tachyons
ada support
globenewswire
relay42
rubicon project
salsa crm
wpforms
sap afaria
edsby
geckoboard
osano
floify
sakon
aws single sign-on
phusion passenger
blue state
bloomberg vault
tucows
give lively
servicesource
firehydrant
eagle eye networks
metorik
glds
inspectlet
datagrail
profitwell
barracuda message archiver
pixabay
pbhs
mathjax
rave alert
reconart
masterbase
addthis
skyprep
telepacific
cafepress
kantox
talkspirit
akamai dns
forest admin
financialforce accounting
statcounter
loyaltylion
big cartel
infor enterprise asset
citrix podio
tvsquared
phoneburner
artifi
genedata biologics
thrive themes
salsa engage
optimonk
centercode
sharethrough
apple icloud
oracle primavera suite
govx
boldchat
sharethis
brandmaker
mopinion
recruiterbox
photobucket
sitelock
moz local
sap ecc r/3
disqus
reachlocal
madkudu
name.com
oracle data mining
dns made easy
readytalk
sharetribe
shopper approved
classmarker
mcgraw-hill connect
rebrandly
calibermind
smile.io
monsterinsights
twik
register.com
elfsight
sirv
turbolinks
mangoapps
opensrs
interviewstream
tweenmax
xenforo
signrequest
publitas
parseur
dialogtech
oracle iot
elastic email
placester
manhattan order management
simplenexus
urx
adwerx
ryver
marketwired
google publisher tag
solvvy
sas data quality
firespring
macabacus
launchcode
qualfon
learnvest
litespeed web server
smartsupp
helpwise
springer-miller systems
rackspace email
fortigate cloud
passle
imgix
fastmail
gmo internet
bold commerce
mentorcliq
sap fieldglass vendor
clickcease
poptin
revenuewell
a2 hosting
simplybook.me
athenacollector
squirrelmail
ca auditor
synxis central reservation
callpage
curve hero
hello bar
primavera p6 cloud
pushowl
transip
typekit
paynearme
azure translator
ethnio
loopme
snipcart
foundant technologies
organimi
50skills
123formbuilder
zift solutions
attentive mobile
catalant
getsitecontrol
namesilo
loopia
first american insurance
fis data integrity
bookafy
kickfire
godaddy dns
spinify
docsify
infolinks
chargeafter
infosec skills
smartwaiver
cupix
stylelabs
google vpc
opentext process suite
reviews.co.uk
dealer spike
blizz
retargeter
acquiretm
fathom analytics
upcall
convirza
no-ip
just host
sift science
loyal health
owl carousel
script.aculo.us
nameshield
goaffpro
akismet
pointdrive
channelsight
hostway
audioeye
sitescout
buysellads
vision-box
register.it
sendwithus
black diamond wealth
fileinvite
godaddy website builder
myfonts
envirosoft
mimecast gateway
turnkey lender
ink for all
popper.js
nsone
growbots
sap crystal server
rmail
tour builder
clarity ca
engageware
securedrop
glyphicons
blippar
pure chat
amazon workmail
brandkit
fast simon
fundrise
fastory
groovehq
mailchannels
prosites
clarity.fm
inmotion hosting
taplytics
web app manifest
afternic
showpad coach
real geeks
inzpire.me
temp mail
sproutloud
miarec
ssl.com
activedemand
fmgsuite
icims recruit
exelate
kontakt.io
gumlet
ariba procurement solutions
shufflrr
cardinalcommerce
the nortridge loan
google blogger
meta graph api
yandex mail
mailparser
netregistry
sesame communications
hostpapa
openprovider
access global group
ixact contact
roanoke insurance group
yoast plugins
giosg
helpsite
smartrmail
wpmu dev
eshares
appboy
ipinyou
gfi archiver
livecall
targetbay
emailmeform
dnsimple
ip info
deviceatlas
spincar
thomson reuters practice
webpurify
neustar ultradns
oracle billing &
mailmunch
hyprmx
opinion stage
snap.svg
ipostal1
basekit
sas visual text
zonka
oneall
fellowshipone
united domains
mailoptin
lendfoundry
sitewit
dotdigital engagement cloud
jalbum
widgetic
dreamscape networks
unpkg
elementor.com
packery
glassdoor jobs
above.com
anytime mailbox
easydns
socketlabs
hostpoint
nextgen gallery
corelogic credco
123contactform
silkroad performance
postcardmania
total.js
csod recruiting
collect.chat
allencomm
odesk and elance
hostmonster
mux.com
cm4all
matelso
smart moderation
zoomforth
re 2.0
gstatic
wp smush
sweetrush
crelate talent
regroup mass notification
heapanalytics
advertserve
mobusi
envira gallery
forextrader
mapegy
bing universal event
yoose
childcarecrm
canddi
light4
fermat cad
wordstream advisor
namecheap private email
proboards
roximity
keen io
inline manual
buysafe
forcepoint triton apx
parserr
agentiq
ez360
inwx
aruba dns
cdnjs
sherpadesk
ibe.net
commlab india
sonobi
sap crystal solutions
flexslider
distributor data solutions
thomson reuters prolaw
envoy visitors
ubermenu
jomsocial
quantcast measure
linkminer
report uri
jersey watch
addtoany
sapenta
atlassian halp
digitalocean dns
flickfusion
gemalto safenet keysecure
hubspot website platform
logrhythm nextgen siem
help desk migration
xinnet
websitebox
tahometer
cloudns
callbackhunter
ipinfo.io
visitor queue
uszoom
yahoo business mail
inkxe
firmseek
```
## Countries
Used by `lead_location` (when the value has no comma) and `company_hq_location`. **209 values.**
`lead_location` accepts both country names (matched against this list) and full addresses (e.g. `"Paris, France"`). `company_hq_location` accepts country names or full addresses. The presence of a comma routes to the address path.
```
United States
Canada
United Kingdom
Australia
Afghanistan
Albania
Algeria
Andorra
Angola
Antigua and Barbuda
Argentina
Armenia
Austria
Azerbaijan
Bahamas
Bahrain
Bangladesh
Barbados
Belarus
Belgium
Belize
Benin
Bhutan
Bolivia
Bonaire, Sint Eustatius, and Saba
Bosnia and Herzegovina
Botswana
Brazil
Brunei
Bulgaria
Burkina Faso
Burundi
Cabo Verde
Cambodia
Cameroon
Central African Republic
Chad
Chile
China
Cocos (Keeling) Islands
Colombia
Comoros
Congo
Costa Rica
Croatia
Cuba
Cyprus
Czechia
Côte d'Ivoire
Democratic Republic of the Congo
Denmark
Djibouti
Dominica
Dominican Republic
Ecuador
Egypt
El Salvador
Equatorial Guinea
Eritrea
Estonia
Eswatini
Ethiopia
Fiji
Finland
France
Gabon
Gambia
Georgia
Germany
Ghana
Greece
Grenada
Guatemala
Guinea
Guinea-Bissau
Guyana
Haiti
Heard and McDonald Islands
Holy See
Honduras
Hong Kong
Hungary
Iceland
India
Indonesia
Iran
Iraq
Ireland
Israel
Italy
Jamaica
Japan
Jersey
Jordan
Kazakhstan
Kenya
Kiribati
Kosovo
Kuwait
Kyrgyzstan
Laos
Latvia
Lebanon
Lesotho
Liberia
Libya
Liechtenstein
Lithuania
Luxembourg
Madagascar
Malawi
Malaysia
Maldives
Mali
Malta
Marshall Islands
Mauritania
Mauritius
Mexico
Micronesia
Moldova
Monaco
Mongolia
Montenegro
Morocco
Mozambique
Myanmar
Namibia
Nauru
Nepal
Netherlands
New Zealand
Nicaragua
Niger
Nigeria
North Korea
North Macedonia
Norway
Oman
Other
Pakistan
Palau
Palestine State
Panama
Papua New Guinea
Paraguay
Peru
Philippines
Poland
Portugal
Qatar
Romania
Russia
Rwanda
Saint Kitts and Nevis
Saint Lucia
Saint Pierre and Miquelon
Saint Vincent and the Grenadines
Samoa
San Marino
Sao Tome and Principe
Saudi Arabia
Senegal
Serbia
Seychelles
Sierra Leone
Singapore
Slovakia
Slovenia
Solomon Islands
Somalia
South Africa
South Georgia and South Sandwich Islands
South Korea
South Sudan
Spain
Sri Lanka
Sudan
Suriname
Svalbard and Jan Mayen
Sweden
Switzerland
Syria
Taiwan
Tajikistan
Tanzania
Thailand
Timor-Leste
Togo
Tonga
Trinidad and Tobago
Tunisia
Turkey
Turkmenistan
Tuvalu
U.S. Outlying Islands
Uganda
Ukraine
United Arab Emirates
Uruguay
Uzbekistan
Vanuatu
Venezuela
Vietnam
Wallis and Futuna
Yemen
Zambia
Zimbabwe
Åland
```
# Webhooks
Source: https://doc.bettercontact.rocks/api-reference/webhooks
Get results pushed to you instead of polling
Every async endpoint accepts a `webhook` attribute. When you provide one, BetterContact posts the
results to that URL as soon as the request is done, and you never have to poll.
```json Request theme={null}
{
"webhook": "https://example.com/hooks/bettercontact",
"enrich_email_address": true,
"data": [
{ "first_name": "Elon", "last_name": "Musk", "company_domain": "tesla.com" }
]
}
```
## How the call is made
| | |
| - | - |
| Method | `POST` |
| Content type | `application/json` |
| Body | Exactly the payload you would get from the matching `GET` endpoint |
| Success | Any `2xx` response |
| Retries | Up to 5 attempts, then the request is abandoned |
| Backoff | 10s after the 1st failure, then 2min, 3min, 4min |
| Timing | Sent once, when the whole request reaches `terminated` |
The body of the enrichment webhook is identical to
[`GET /async/{request_id}`](/api-reference/endpoint/get), and the body of the Lead Finder webhook
is identical to
[`GET /lead_finder/async/{request_id}`](/api-reference/endpoint/lead_finder_get). There is no
separate envelope and no event type to switch on.
Answer `200` as soon as you have persisted the payload, and do the actual processing afterwards. A
slow endpoint that times out counts as a failed attempt.
## No signature
Webhook calls carry **no signature header and no authentication**. Treat the webhook URL itself as a
secret: give it a long unguessable path, and do not expose it anywhere public.
If you need certainty about the origin of a payload, do not trust the body directly. Read the `id`
it carries and call `GET /async/{id}` with your API key to confirm.
## Per lead webhooks
On the enrichment API, `push_contact_individually` changes the delivery model: instead of one call
with the whole batch at the end, each lead is pushed to `contact_webhook` as soon as it is enriched.
This is useful for batches of 100 where you would rather start working on the first results without
waiting for the slowest lead.
```json Request theme={null}
{
"webhook": "https://example.com/hooks/bettercontact",
"contact_webhook": "https://example.com/hooks/bettercontact/contact",
"push_contact_individually": true,
"enrich_email_address": true,
"data": [ "..." ]
}
```
The payload sent to `contact_webhook` is a **single flat object**, not the batch envelope. It always
carries `request_id`, `enriched`, `custom_fields` and the enriched contact fields.
Note that `custom_fields` comes back as an array of `{ name, value, position }` entries even though
you sent an object. Look your field up by `name`.
```json Per lead payload theme={null}
{
"request_id": "fefbc2203558eb3adcea",
"enriched": true,
"contact_first_name": "Elon",
"contact_last_name": "Musk",
"contact_email_address": "elon@tesla.com",
"contact_email_address_status": "deliverable",
"company_domain": "tesla.com",
"custom_fields": [{ "name": "uuid", "value": "crm-8821", "position": 0 }]
}
```
Per lead calls are **not retried**. Keep `webhook` set alongside `contact_webhook` so the full
batch payload still reaches you at the end, and use it to reconcile anything you missed.
## Testing
Point `webhook` at a request bin while you build, then submit a single lead. Every attempt is logged
on our side with its HTTP status and the response body we received, so if nothing arrives, open the
request in [your BetterContact account](https://app.bettercontact.rocks) or ask support to read the
attempts back to you.
# Quickstart
Source: https://doc.bettercontact.rocks/quickstart
Find and enrich your leads with the most powerful waterfall aggregator on the market
## What does BetterContact do?
Based on advanced **Company** and **People** filtering, find the perfect leads matching your criteria, with only fresh and accurate data.
Based on **firmographic**, **funding** and **hiring** filters, build a list of the accounts matching your ideal customer profile.
Get the **mobile phone number** and **work email address** of the leads you want to reach, and convert them into customers.
Look up **one person or one company** and get their full profile back in the same response, with no polling.
## Your first call
Grab your API key in [your API settings](https://app.bettercontact.rocks/api_requests), then enrich
a single lead:
```bash cURL theme={null}
curl -X POST https://app.bettercontact.rocks/api/v2/async \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"enrich_email_address": true,
"enrich_phone_number": false,
"data": [
{ "first_name": "Elon", "last_name": "Musk", "company_domain": "tesla.com" }
]
}'
```
```javascript Node.js theme={null}
const res = await fetch("https://app.bettercontact.rocks/api/v2/async", {
method: "POST",
headers: {
"X-API-Key": process.env.BETTERCONTACT_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
enrich_email_address: true,
enrich_phone_number: false,
data: [{ first_name: "Elon", last_name: "Musk", company_domain: "tesla.com" }],
}),
});
const { id } = await res.json(); // this is your request_id
```
```python Python theme={null}
import os, requests
res = requests.post(
"https://app.bettercontact.rocks/api/v2/async",
headers={"X-API-Key": os.environ["BETTERCONTACT_API_KEY"]},
json={
"enrich_email_address": True,
"enrich_phone_number": False,
"data": [{"first_name": "Elon", "last_name": "Musk", "company_domain": "tesla.com"}],
},
)
request_id = res.json()["id"]
```
You get a `request_id` back immediately. Enrichment runs in the background.
**Both APIs are asynchronous, and `2xx` does not mean the results are ready.**
While a request is running, the `GET` endpoints answer `202` with an empty body. Only
`status: "terminated"` guarantees results. Branch on `status`, never on the HTTP code alone.
See [Request statuses](/api-reference/statuses).
Then either poll [`GET /async/{request_id}`](/api-reference/endpoint/get), or, better, pass a
[`webhook`](/api-reference/webhooks) in the submit call and let us push the results to you.
## Waterfall Lead Finder search
### Data you get back
Up to date contact information: job title, full name, LinkedIn profile URL, location, seniority, and more.
Up to date information on the contact's current company: name, domain, HQ location, industry, headcount, and more.
### Principles
Company criteria (industry, headcount, HQ, technologies, funding, hiring signals) and people criteria (job title, seniority, department, skills, location) are applied in the same search. See the full list on the [endpoint reference](/api-reference/endpoint/lead_finder_post).
Every list filter takes an `include` and an `exclude` array. Exclusions are enforced by BetterContact after the search runs, which is why a request made only of `exclude` values matches nothing: always pair it with at least one `include`.
`limit_per_company` caps how many leads come back per company, so a search returns a slice of many accounts instead of one full org chart. Combine it with `limit` and `offset` to paginate through the whole result set.
### How the API works
Apply your People and Company criteria and create a new search.
[See endpoint](/api-reference/endpoint/lead_finder_post)
Several filters only accept exact values from a fixed list. See [Taxonomies](/api-reference/taxonomies).
Provide a webhook URL, or fetch the leads matching your criteria.
[See endpoint](/api-reference/endpoint/lead_finder_get)
Set `enrich_email_address` or `enrich_phone_number` on the search itself to get verified contact
data on every lead found, without a second call to the enrichment API.
## Company Finder search
Company Finder returns **companies**, not people: use it to build a target account list before
looking for the right contacts in them.
### How the API works
Apply your company criteria (industry, headcount, HQ, technologies, funding, hiring signals) and
create a new search.
[See endpoint](/api-reference/endpoint/company_finder_post)
Fetch the companies matching your criteria. Company Finder does not send webhooks.
[See endpoint](/api-reference/endpoint/company_finder_get)
Company Finder costs **0.1 credit per company returned**. A search that matches nothing is free.
## Waterfall email and phone enrichment
### Data you get back
Get any lead's verified mobile phone number.
Get any lead's verified work email address.
### Principles
According to the information provided about the lead, BetterContact determines the optimal provider sequence to maximise the enrichment rate.
Once a provider returns contact data, we verify it through various layers and only push back valid data to you.
If one provider's data is invalid, BetterContact will request the following providers until valid data is found.
**BetterContact also verifies catch-all emails.**
Set `verify_catch_all` to `true` and the status comes back as `catch_all_safe` or
`catch_all_not_safe` instead of a plain `catch_all`, so you can contact otherwise unreachable
leads without compromising your sending domain reputation.
### How the API works
A single endpoint enriches one lead or a batch of up to 100 at a time.
[See endpoint](/api-reference/endpoint/create)
The waterfall needs a few seconds to query the providers and verify the data. Fetch the results,
or pass a `webhook` URL in the submit call to get them pushed to you.
[See endpoint](/api-reference/endpoint/get)
## Enrich profiles
The two endpoints above are asynchronous, because a waterfall takes time. When you only need to look
up **one** person or **one** company, Enrich Profile answers straight away.
Job title, seniority, location, LinkedIn profile, and the company the person works at.
**0.1 credit** per profile found.
Industry, headcount, headquarters, description, keywords, LinkedIn page.
**Free**, no credit consumed.
### How it differs from the rest of the API
One call, one answer. The profile is in the response body under `data`, so there is nothing to poll and no webhook to set up.
These endpoints never return a work email address or a mobile phone number. For contact data, use the [waterfall enrichment](#waterfall-email-and-phone-enrichment) endpoint.
A lookup that returns no match answers `404` and costs nothing. See [Credits](/api-reference/credits).
For a lead, send the contact's `linkedin_url` whenever you have it: it is the most reliable input.
Otherwise send `first_name`, `last_name` and `company_domain` together.
For a company, send `company_domain`, `company_linkedin_url`, or both.
## Next steps
The `X-API-Key` header, and where to find your key.
Know when a request is really done.
Stop polling, get results pushed to you.
Every status code, and what to do about it.
When a credit is consumed, and what happens when you run out.