# 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.