Enrichment and Prospecting
These endpoints bring data from a data provider into your workspace. Enrichment fills in contacts and companies you already have. Prospecting finds people at companies you already track, or searches the provider’s database for companies and people you do not have yet and saves the matches as records.
Two providers can run a call: Apollo and Hunter. You never name one. Pathbound picks the workspace’s connected provider, and every response says which one ran in via.
Before the first call, connect Apollo or Hunter under Integrations, have a workspace admin turn on the switch for the kind of work under MCP Tools → Apollo or MCP Tools → Hunter, and use an API key that has the enrichment:write scope. The MCP tools enrich_contacts, enrich_companies, find_people_at_companies, prospect_people and prospect_companies call these endpoints (tools reference).
| Endpoint | Scope | What it does |
|---|---|---|
POST /v1/enrichment/contacts | enrichment:write | Enrich up to 25 contacts you already have. |
POST /v1/enrichment/companies | enrichment:write | Enrich up to 25 companies you already have. |
POST /v1/prospecting/people-at-companies | enrichment:write | Find people at up to 25 companies you already have and save them as contacts. |
POST /v1/prospecting/people | enrichment:write | Search for new people by person and company filters and save them with their employers. |
POST /v1/prospecting/companies | enrichment:write | Search for new companies by filters and save them. |
enrichment:write is opt-in. A key has it only when it was selected at creation, and a key created without any scopes does not have it.
Providers and switches
Section titled “Providers and switches”A workspace admin decides what each provider may be used for. Each provider has three switches under MCP Tools:
| Switch | Endpoints it allows |
|---|---|
| Enrichment | POST /v1/enrichment/contacts, POST /v1/enrichment/companies |
| People discovery | POST /v1/prospecting/people-at-companies |
| Prospecting | POST /v1/prospecting/people, POST /v1/prospecting/companies |
The API reads these switches on every call and for every caller. A REST API key is held to them exactly as an MCP connection is. The MCP server also reads them to decide which tools it lists. When no connected provider has the switch on for the kind of work you ask for, the call is answered 412 and nothing is spent.
When both providers are connected and both allow the kind of work, Apollo runs the call. To have Hunter run it, leave Apollo’s switch for that kind of work off. One provider runs a call: if it finds nothing, the other is not tried. No field names a provider, and one sent in the body is not read.
Enrich Contacts
Section titled “Enrich Contacts”POST /v1/enrichment/contactsRequired scope: enrichment:write
Looks each contact up with the provider and merges what it returns into the contact: name, job title, LinkedIn profile URL, phone, location, company and, when the provider reveals or finds one, the email address.
Request Body
Section titled “Request Body”| Field | Type | Required | Description |
|---|---|---|---|
contact_ids | string[] | yes | 1 to 25 contact IDs. Duplicates count once. |
force | boolean | no | Enrich contacts that were already enriched. This spends credits again. Default false. |
reveal_personal_emails | boolean | no | Also reveal personal (non-work) email addresses, which may use more credits. Default false. Apollo only: Hunter returns work addresses. |
curl -X POST https://api.pathbound.ai/v1/enrichment/contacts \ -H "Authorization: Bearer $PATHBOUND_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "contact_ids": [ "665f1c2ab3d4e5f601234567", "665f1c2ab3d4e5f601234568", "665f1c2ab3d4e5f601234569" ] }'How a contact is looked up depends on the provider:
- Apollo matches on the contact’s email, name, company and LinkedIn profile URL.
- Hunter enriches a contact that has an email from that address. For a contact without one it looks for a work address, from the LinkedIn profile URL or from the first and last name plus the company’s domain or name. A contact with none of these is skipped with
insufficient_identity.
Response — 200 OK
Section titled “Response — 200 OK”{ "status": "success", "via": "apollo", "requested": 3, "enriched": 1, "not_found": 1, "skipped": 1, "credits_consumed": 1, "deliverability": "queued", "results": [ { "contact_id": "665f1c2ab3d4e5f601234567", "status": "enriched", "email_status": "verified" }, { "contact_id": "665f1c2ab3d4e5f601234568", "status": "not_found" }, { "contact_id": "665f1c2ab3d4e5f601234569", "status": "skipped", "reason": "already_enriched" } ]}| Field | Description |
|---|---|
via | The provider that ran the call: apollo or hunter. |
requested | Distinct contact IDs in the request. |
enriched, not_found, skipped | How many results ended in each status. |
credits_consumed | Provider credits spent, in that provider’s own units. See Limits and costs. |
deliverability | What happens to new addresses. See Deliverability. |
results[].status | enriched, not_found (the provider has no match) or skipped. |
results[].reason | Why a contact was skipped. |
results[].email | The contact’s address when the provider revealed, found or enriched from one. |
results[].email_status | The provider’s own status for that address, in the provider’s words. |
A request that reaches a provider is a 200, even when no contact was enriched. Read results to see what happened to each one.
reason | Meaning |
|---|---|
not_found_in_pathbound | No contact with this ID in your workspace. |
already_enriched | The contact was enriched before and force is not set. |
insufficient_identity | Hunter: the contact has no email, and no LinkedIn profile URL or name plus company to search with. |
email_belongs_to_contact:<contact_id> | Hunter found an address that another contact already holds. Neither contact is changed. |
claimed_email | The person asked Hunter to stop processing their data. The contact is never sent to Hunter again, force included. |
time_budget | Hunter: the call stopped starting new lookups after 11 seconds. Call again for these contacts. |
apollo_error: …, hunter_error: … | The provider failed for this contact. |
persist_error: … | The provider answered and the result could not be saved. |
With Hunter, when the key is refused, the credits are used up or the rate limit is reached before any contact got through, the whole call is answered with that refusal instead of a list of skipped contacts.
Enrich Companies
Section titled “Enrich Companies”POST /v1/enrichment/companiesRequired scope: enrichment:write
Looks each company up by its domain and merges what the provider returns into the company: name, industry, description, location, phone, employee count, annual revenue and founded year.
Request Body
Section titled “Request Body”| Field | Type | Required | Description |
|---|---|---|---|
company_ids | string[] | yes | 1 to 25 company IDs. Duplicates count once. |
force | boolean | no | Enrich companies that were already enriched. This spends credits again. Default false. |
Response — 200 OK
Section titled “Response — 200 OK”{ "status": "success", "via": "hunter", "requested": 2, "enriched": 1, "not_found": 0, "skipped": 1, "credits_consumed": 0.2, "results": [ { "company_id": "665f1c2ab3d4e5f6012345a1", "status": "enriched", "domain": "example.com" }, { "company_id": "665f1c2ab3d4e5f6012345a2", "status": "skipped", "reason": "no_domain" } ]}The fields are those of Enrich Contacts, with company_id and the company’s domain on each result and no deliverability.
reason is not_found_in_pathbound, already_enriched, no_domain (the company has no domain to look up), time_budget, or one of the apollo_error: …, hunter_error: … and persist_error: … forms.
Find People at Companies
Section titled “Find People at Companies”POST /v1/prospecting/people-at-companiesRequired scope: enrichment:write
Finds people who work at companies already in your workspace and saves them as contacts linked to those companies. Pathbound reads each company’s domain from its record and searches the provider within that domain, in the order you gave the companies, until limit people are collected.
Request Body
Section titled “Request Body”| Field | Type | Required | Description |
|---|---|---|---|
company_ids | string[] | yes | 1 to 25 company IDs. |
titles, seniorities, departments, locations, keywords, days_in_title_min, days_in_title_max | no | Person filters. Without any, everyone the provider finds at the company matches. | |
limit | number | no | The most people to save across all companies: 1 to 25, default 10. |
reveal_emails | boolean | no | Get each person’s work email. Default true. |
reveal_personal_emails | boolean | no | Also reveal personal email addresses, which may use more credits. Default false. Apollo only. |
tags | string[] | no | Tags for what the call saves. |
lifecyclestage | string | no | Stage written on contacts this call creates. No default: without it a new contact gets no stage. |
Company filters do not apply here. The companies are the filter, and an org_* field is not read.
curl -X POST https://api.pathbound.ai/v1/prospecting/people-at-companies \ -H "Authorization: Bearer $PATHBOUND_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "company_ids": ["665f1c2ab3d4e5f6012345a1"], "titles": ["head of operations", "vp of operations"], "limit": 5, "tags": ["campaign:ops-leaders"] }'The two providers work differently:
- Apollo searches for free and charges for each email it reveals. A person your workspace already got from Apollo and holds an email for is skipped before anything is revealed, and counted in
existing_skipped. Withreveal_emails: falsepeople are saved without an email at no cost. A later call with reveals on reveals those same people. - Hunter returns people together with their addresses, up to 10 per company, and charges per company that has matches. With
reveal_emails: falseit searches nothing, spends nothing and returns no one. It cannot skip people before it charges, soexisting_skippedis always0. A person already in your workspace comes back withcreated: false.
Response — 200 OK
Section titled “Response — 200 OK”{ "status": "success", "via": "apollo", "mode": "anchored", "companies": [ { "company_id": "665f1c2ab3d4e5f6012345a1", "domain": "example.com", "found": 2, "created": 2, "existing": 0 } ], "contacts": [ { "contact_id": "670a3b4c5d6e7f8091a2b3c4", "company_id": "665f1c2ab3d4e5f6012345a1", "provider_person_id": "5f2a9c1e8b3d4a0001c7e9b2", "name": "Ada Lovelace", "title": "VP of Operations", "email_status": "verified", "created": true } ], "requested_limit": 5, "search_total": null, "existing_skipped": 0, "companies_created": 0, "unlinked": 0, "credits_consumed": 2, "deliverability": "queued", "tags_applied": ["campaign:ops-leaders"], "note": "Contacts saved. Email deliverability (Emailable) is being checked in the background …"}| Field | Description |
|---|---|
via | The provider that ran the call. |
mode | anchored on this endpoint, unanchored on Find New People. |
companies[] | One entry per company: company_id, domain, name when known, how many people were found, how many contacts were created, and how many were existing. |
companies[].skipped_reason | Why a company was not searched: not_found_in_pathbound, no_domain, limit_reached (the limit was filled by earlier companies), reveal_required (Hunter with reveal_emails: false), time_budget, apollo_error: … or hunter_error: …. |
companies[].company_created | On Find New People: whether this call created the company. |
contacts[] | The people saved: contact_id, company_id, name, title, email, email_status (the provider’s own status for the address) and created (false when the person matched a contact you already had). |
contacts[].provider_person_id | Apollo’s ID for the person. Not present on Hunter, which has none. |
requested_limit | The limit that applied. |
search_total | How many people match in the provider’s whole database, when it reports one. null on this endpoint. |
existing_skipped | People passed over before any reveal, because your workspace already got them from Apollo and holds an email for them. Always 0 on Hunter. |
companies_created | Companies this call created. Always 0 on this endpoint. |
unlinked | Contacts saved without a company because the provider returned no employer domain. |
credits_consumed | Provider credits spent. |
deliverability | See Deliverability. |
tags_applied | The tags written, lowercased. Empty when nothing was tagged. |
note | A sentence about the outcome, written for a person or an AI agent. Do not parse it. |
Find New People
Section titled “Find New People”POST /v1/prospecting/peopleRequired scope: enrichment:write
Searches the provider’s database for people by who they are and by what kind of company they work at, and saves the matches as contacts. Each person’s employer is matched to a company in your workspace by its domain, or created, and the contact is linked to it.
At least one company filter is required. A search by person filters alone is refused with 400 unanchored_requires_org_filter.
Request Body
Section titled “Request Body”| Field | Type | Required | Description |
|---|---|---|---|
org_employee_ranges, org_locations, org_keywords, org_technologies, org_hiring_titles, org_hiring_posted_after, org_domains | at least one | Company filters, written with the org_ prefix on this endpoint. | |
titles, seniorities, departments, locations, keywords, days_in_title_min, days_in_title_max | no | Person filters. | |
limit | number | no | The most people to save: 1 to 25, default 10. |
reveal_emails | boolean | no | Get each person’s work email. Default true. |
reveal_personal_emails | boolean | no | Also reveal personal email addresses, which may use more credits. Default false. Apollo only. |
tags | string[] | no | Tags for what the call saves. |
lifecyclestage | string | no | Stage written on the contacts and companies this call creates. Default prospect. |
{ "titles": ["head of sales"], "org_keywords": ["field service"], "org_employee_ranges": ["11,50", "51,200"], "org_locations": ["United States"], "limit": 10, "tags": ["icp:field-service"]}On this endpoint locations and keywords describe the person. The company’s are org_locations and org_keywords.
The two providers work differently here too:
- Apollo runs one free search and reveals up to
limitof the people it finds, passing over those your workspace already got from Apollo with an email (existing_skipped).search_totalis the number of people who match in Apollo’s database. A person whose employer has no domain is saved without a company and counted inunlinked. - Hunter first finds companies that match the company filters, for free, then looks for people inside up to 15 of them and takes up to 3 from each.
search_totalisnull, andnotesays how many companies matched.
Response — 200 OK
Section titled “Response — 200 OK”The response has the shape of Find People at Companies, with mode: "unanchored". companies lists the employers of the people saved, each with company_created.
Find New Companies
Section titled “Find New Companies”POST /v1/prospecting/companiesRequired scope: enrichment:write
Searches the provider’s database for companies and saves the matches as companies in your workspace. A company you already have, matched by domain, is returned with created: false and left as it is.
At least one filter is required. A request without one is refused with 400 requires_org_filter.
Request Body
Section titled “Request Body”| Field | Type | Required | Description |
|---|---|---|---|
employee_ranges, locations, keywords, technologies, hiring_titles, hiring_posted_after, domains, name | at least one | Company filters, without a prefix on this endpoint. | |
limit | number | no | The most companies to save: 1 to 50, default 25. On Apollo it does not change the cost of the call. |
tags | string[] | no | Tags for the companies this call creates. |
enrich | boolean | no | Also enrich the companies this call creates, the first 25 of them, at the price of an enrichment each. Default false. |
lifecyclestage | string | no | Stage written on the companies this call creates. Default prospect. |
curl -X POST https://api.pathbound.ai/v1/prospecting/companies \ -H "Authorization: Bearer $PATHBOUND_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "keywords": ["field service"], "employee_ranges": ["11,50", "51,200"], "locations": ["United States"], "limit": 25, "tags": ["icp:field-service"] }'Response — 200 OK
Section titled “Response — 200 OK”{ "status": "success", "via": "apollo", "search_total": 1840, "requested_limit": 25, "found": 25, "created": 22, "existing": 2, "companies": [ { "company_id": "670a3c5d6e7f8091a2b3c4d5", "provider_company_id": "5e66b6381e05b4008c8331b8", "domain": "example.com", "name": "Example Field Services", "created": true, "saved_in_provider": false } ], "skipped": [ { "provider_company_id": "5e66b6381e05b4008c8331c9", "name": "Example Holdings", "reason": "no_domain" } ], "credits_consumed": 1, "tags_applied": ["icp:field-service"], "note": "Saved 22 new prospect companies (2 already in Pathbound). …"}| Field | Description |
|---|---|
search_total | How many companies match in the provider’s whole database, when it reports one. Use it to judge the filters before you ask for more. |
found | Companies the provider returned for this call, at most limit. |
created, existing | How many of them this call created, and how many you already had. |
companies[].company_id | The Pathbound company ID. Pass these to Find People at Companies. |
companies[].provider_company_id | The provider’s own ID for the company. On Hunter it is the domain. |
companies[].saved_in_provider | true when the company was already saved as an account in your Apollo team. Always false on Hunter. |
skipped | Companies the provider returned without a domain. They are reported and never saved. |
enrichment | Present when enrich was true and at least one company was created: the Enrich Companies result for those companies. |
The call returns one page of results. To get different companies, change the filters.
On Apollo this search needs an Apollo connection made after September 11, 2026. An older connection is answered 412 scope_missing: reconnect Apollo under Integrations.
With enrich: true, the search and the save happen first. If the enrichment step is then refused for the daily budget, the call is answered 429 although the companies were already saved.
Filters
Section titled “Filters”A filter that the provider running the call cannot express is refused before anything is spent. It is never dropped, because a dropped filter widens the search and you would pay for the wider result. The same goes for a filter value the provider cannot express exactly.
Up to 50 values are read from each list (employee_ranges 20, domains 100), and values past that are not read. A company list of more than 100 values is a 400.
Every entry of a list has to be a string. An entry of another type is not read and raises no error, so "employee_ranges": [[11, 50]] is an empty filter, and a search that has other filters then runs without it. Check the types you send: this is the one case in which a filter can go missing.
Company filters
Section titled “Company filters”On POST /v1/prospecting/companies the fields are written as below. On POST /v1/prospecting/people each takes the org_ prefix (org_employee_ranges, org_locations, and so on), and name is not available.
| Field | Type | Description |
|---|---|---|
employee_ranges | string[] | Employee-count ranges, each a string written "min,max", for example ["11,50", "51,200"]. A string in any other form is a 400 invalid_employee_range. |
locations | string[] | Where the company is headquartered: cities, states or countries, for example ["United States", "Berlin, Germany"]. Give a city its country. |
keywords | string[] | Keyword tags the provider files companies under, for example ["field service", "dispatch software"]. A company matches when it has any of them. |
technologies | string[] | Technologies the company uses, for example ["hubspot", "salesforce"]. A company matches when it uses any of them. Apollo reads each value as one of its technology identifiers, written with underscores for spaces and dots. |
hiring_titles | string[] | Titles in the company’s open job postings, for example ["sales development representative"]. |
hiring_posted_after | string | Only companies with a job posting from this date on, as YYYY-MM-DD. Any other form is a 400 invalid_date. |
domains | string[] | Only these company domains, for example ["example.com"]. The companies need not be in your workspace. |
name | string | Part of a company name, up to 200 characters. POST /v1/prospecting/companies only. |
Person filters
Section titled “Person filters”| Field | Type | Description |
|---|---|---|
titles | string[] | Job titles, for example ["head of sales", "vp of customer success"]. |
seniorities | string[] | Seniority levels, in the provider’s vocabulary. Apollo’s include c_suite, vp, director and manager. Hunter has three: junior, senior, executive. |
departments | string[] | Departments, in the provider’s vocabulary, for example ["sales", "marketing"]. |
locations | string[] | Where the person is based. Independent of the company’s location. |
keywords | string | Free text to match on the person, up to 300 characters. |
days_in_title_min, days_in_title_max | number | Days in the current title, 0 to 36500. A days_in_title_max of 90 finds people new in their role. |
What Hunter cannot express
Section titled “What Hunter cannot express”Apollo can express every filter above. Hunter cannot. When Hunter is the provider for a call, these are refused:
- Person
keywords,days_in_title_min,days_in_title_max. Not supported. The call is refused with400 unsupported_filters. employee_ranges. Hunter has fixed company sizes: 1-10, 11-50, 51-200, 201-500, 501-1000, 1001-5000, 5001-10000 and 10001+. A range must start and end on those edges:"11,50"and"51,500"are accepted,"20,100"is not. For no upper bound, end the range at1000000or more, as in"10001,1000000".seniorities. Onlyjunior,seniorandexecutive. Name specific roles withtitlesinstead.departments. Onlyexecutive,it,finance,management,sales,legal,support,hr,marketing,communication,education,design,health,operations,product,research,consulting,administrativeandprocurement. Engineering roles are underit.locations, the company’s and the person’s. Hunter takes a country by name or ISO code ("Germany","FR"), a US state ("California"), a city with its country or state ("Berlin, Germany","Austin, Texas"), a continent ("Europe"), or one of the regionsAMER,EMEA,APACandLATAM. A city on its own is refused, and so is a name that is both a US state and a country: write"Georgia, US"for the state.hiring_posted_after. Hunter filters job postings by the last 7, 30 or 90 days only. Your date is applied as the widest of those windows that fits inside it, so the search is never wider than you asked, andnotesays which window was used. A date less than 7 days back is refused.nametogether withdomains. Refused. Search by one or the other.technologies. Needs a paid Hunter plan. Without one the call is answered412 plan_required.
A value Hunter cannot express is refused with 400 unsupported_filter_value. The error names the filter and the value in details, and its hint says what Hunter accepts.
What a call writes
Section titled “What a call writes”Records, tags and lifecycle stage
Section titled “Records, tags and lifecycle stage”- A record is matched before it is created. A company is matched by its domain. A person is matched by email address or LinkedIn profile URL. A match is returned with
created: false. - A company is only ever created from a domain. A company the provider returns without one is never saved.
- A person found at a company is linked to that company.
lifecyclestageis written on records the call creates, never on one you already had. A lead or a customer is not turned back into a prospect. The allowed values areprospect,subscriber,lead,mql,sql,opportunity,customer,evangelistandother.tagsare written on the contacts and companies the call creates. On Apollo a contact you already had that the call returns is tagged too. Tags are trimmed, lowercased and cut to 64 characters, and at most 50 are read. Use a tag to get the batch back later withtags_anyonGET /v1/contactsorGET /v1/companies.- Every value is merged into the record with the provider as its source (
apolloorhunter), next to what your other sources say. Read it in the record’s unified profile. - On Hunter a contact you already had keeps its own email address. One that had none receives the address Hunter found.
Deliverability
Section titled “Deliverability”Every response that can add email addresses reports deliverability:
| Value | Meaning |
|---|---|
verified | The provider verified each new address itself, and the verdict is already on the contact as email_verification_state. Hunter does this for the addresses it finds. |
queued | Emailable is checking the addresses that have no verdict, in the background. Read email_verification_state on the contact about a minute later. |
not_connected | The call added no new address, or Emailable is not connected. |
Hunter’s own status becomes the verdict on the contact this way: valid is deliverable, invalid is undeliverable, and accept_all and disposable are risky. Any other status leaves the address without a verdict, for Emailable to check.
GET /v1/contacts filters on the verdict with email_verification_state.
People who asked Hunter to stop
Section titled “People who asked Hunter to stop”When a person has asked Hunter to stop processing their data, the contact is reported skipped with the reason claimed_email, nothing Hunter said about them is kept, and the contact is never sent to Hunter again. If a later search returns that person, the contact is left untouched and note counts them.
Limits and costs
Section titled “Limits and costs”Per call: 25 contacts or companies to enrich, 25 companies to look inside, 25 people to save, 50 companies to save.
Per day: the calls of a workspace, over REST and MCP together, may spend a set number of credits with each provider a day, counted in UTC. The budget is there so that a loop cannot empty your account with the provider. A call whose most expensive outcome would pass it is refused with 429 and the code APOLLO_AGENT_BUDGET or HUNTER_AGENT_BUDGET. details.cap is the budget and details.spent is what was used so far. Nothing is spent by a refused call, and credits a call reserved and did not use are given back. If the budget is too low for your work, write to [email protected].
What each provider charges, in its own credits:
| Apollo | Hunter | |
|---|---|---|
| Enrich a contact | About 1 credit per match. Nothing for a miss. | 0.2 credit for a contact that has an email. 1 credit to find an email for one that has none, and only when one is found. |
| Enrich a company | About 1 credit per match. Nothing for a miss. | 0.2 credit. |
| Find people | The search is free. About 1 credit per email revealed. | 1 credit per company that has matches, for up to 10 people. |
| Find companies | 1 credit per call that returns results. Nothing when nothing matches. | Free. |
credits_consumed on Apollo is what Apollo reported for the call, or the number of matches when Apollo reports nothing. On Hunter it is counted from the prices above. Hunter bills a repeated search once per billing period, so its real charge can be lower.
These endpoints take no Idempotency-Key: a repeated call runs again. A contact or company that was already enriched is skipped unless you set force, and on Apollo a person your workspace already got from it with an email is not revealed twice. An Apollo company search costs its credit every time.
For the API’s own request limits, see Rate limits.
Errors
Section titled “Errors”Checks run in this order, and a call stops at the first one it fails:
- The API key and its scope.
- The shape of the request: types, limits, the form of an ID, an employee range or a date, and a search with no company filter. This answer does not depend on which provider is connected or which switches are on.
- Whether a provider may run the call: connected, and its switch for this kind of work on.
- Whether that provider can express every filter and every filter value.
- The day’s budget, before each step that costs credits.
An error has the shape { "status": "error", "error": "…", "code": "…" }, with a hint and details where the table names them. See Errors for the answers every endpoint shares.
| Status | code | Meaning |
|---|---|---|
400 | none | The body failed validation: a field of the wrong type, a list or a limit out of range, a malformed ID, an unknown lifecyclestage. details lists each field. |
400 | invalid_employee_range | An employee_ranges value is not "min,max". |
400 | invalid_date | hiring_posted_after is not a YYYY-MM-DD date. |
400 | unanchored_requires_org_filter | POST /v1/prospecting/people without any org_* filter. |
400 | requires_org_filter | POST /v1/prospecting/companies without any filter. |
400 | unsupported_filters | No provider that may run the call supports a filter you used. details.unsupported_filters names them, for example person:keywords. Remove the filter, or have an admin turn on a provider that supports it. |
400 | unsupported_filter_value | The provider cannot express a filter value exactly. details.filter and details.value name it and hint says what is accepted. Nothing was spent. |
400 | provider_rejected_request | Hunter refused a filter value. details.hunter_details carries Hunter’s own text. |
403 | none | The API key does not have enrichment:write. required_scopes lists it. |
412 | integration_missing | No data provider is connected. Connect Apollo or Hunter under Integrations. |
412 | tool_disabled | A provider is connected and its switch for this kind of work is off. A workspace admin turns it on under MCP Tools. |
412 | invalid_credentials | The provider refused the connection’s credentials. Reconnect it under Integrations. |
412 | scope_missing | The Apollo connection predates company search. Reconnect Apollo. |
412 | plan_required | The provider’s plan, or on Apollo the permissions of the connection, does not include what the call needs. On Hunter this includes the technologies filter. |
429 | APOLLO_AGENT_BUDGET, HUNTER_AGENT_BUDGET | The workspace’s daily credit budget for these calls would be passed (details.cap, details.spent). It resets at midnight UTC. |
429 | provider_quota_exhausted | The Hunter account has no credits left for its billing period. |
429 | provider_rate_limited | Hunter’s rate limit was reached. Wait a minute and call again. |
451 | claimed_email | The person asked Hunter to stop processing their data. On these endpoints the person is normally reported as skipped, or left untouched, and the call succeeds. |
502 | provider_unavailable | Hunter did not answer. Try again shortly. |
500 | none | The call failed for a reason that has no code. This includes Apollo failing or rate-limiting a search. |
When no provider may run the call, the 412 names the reason that is closest to working: a switch that is off, before credentials that were refused, before a provider that is not connected.
Enrich a whole segment
Section titled “Enrich a whole segment”These endpoints take up to 25 records a call. To enrich every member of a saved segment as a background job, use POST /v1/segments/:ref/actions with the action apollo_enrich. That job always runs on Apollo, under Apollo’s Enrichment switch, and draws on the same daily budget.
Job changes on contacts saved in Apollo
Section titled “Job changes on contacts saved in Apollo”Apollo watches the contacts saved in your Apollo team and records when one changes title or employer. Pathbound reads that from Apollo at no credit cost and writes it on the contact: job_changed_at, previous_jobtitle and previous_company, with jobtitle and company updated to the new values, plus the tag signal:job-change.
To get those contacts, call GET /v1/contacts with tags_any=signal:job-change. Contacts at the prospect stage are checked again about every 30 days once a workspace admin turns on Refresh prospects under Integrations → Apollo.
Earlier Apollo routes
Section titled “Earlier Apollo routes”Four older routes remain for existing callers. New code should use the endpoints above.
| Earlier route | Use instead |
|---|---|
POST /v1/integrations/apollo/contacts/enrich | POST /v1/enrichment/contacts |
POST /v1/integrations/apollo/companies/enrich | POST /v1/enrichment/companies |
POST /v1/integrations/apollo/people/find | POST /v1/prospecting/people-at-companies when the body has company_ids, POST /v1/prospecting/people when it does not |
POST /v1/integrations/apollo/companies/find | POST /v1/prospecting/companies |
They take the same request fields and the same scope. They always run on Apollo, under Apollo’s switches, and never on Hunter. Their responses carry no via and use Apollo’s names: apollo_person_id, apollo_organization_id and saved_in_apollo in place of provider_person_id, provider_company_id and saved_in_provider.