Worked examples
Buyer briefs, and the calls an agent makes for each one.
Each brief below is one sentence a buyer says. Under it are the calls an agent makes, in order, with the body it sends and the shape that comes back. Count first: a count is free. Read the Filters page for every key.
A country, a seniority and a function
We sell to consulting leaders in the United States: size the audience, then show me a few of them.
count_matchesPOST /contacts/count
{
"filter": {
"countries": [
"us"
],
"seniority": [
"c_level",
"vp"
],
"functions": [
"general_management"
],
"has_work_email": true
}
}An envelope whose data.count is a whole number, or null with a count_unavailable warning. The call is free.
find_contactsPOST /contacts/search
{
"filter": {
"countries": [
"us"
],
"seniority": [
"c_level",
"vp"
],
"functions": [
"general_management"
],
"has_work_email": true
},
"columns": [
"id",
"name",
"title",
"company_domain",
"country",
"seniority"
],
"limit": 10
}Up to ten rows in data, a page block with returned, total and next_cursor, and a quota block that is ten contact credits lighter.
A list of target accounts
Show me everyone we can reach by email at our three named target accounts.
count_matchesPOST /contacts/count
{
"filter": {
"domains": [
"acme.example.com",
"northwind.example.net",
"globex.example.org"
],
"has_work_email": true
}
}One number over all three domains, because a list key is OR inside one key.
find_contactsPOST /contacts/search
{
"filter": {
"domains": [
"acme.example.com",
"northwind.example.net",
"globex.example.org"
],
"has_work_email": true
},
"columns": [
"id",
"name",
"title",
"company",
"company_domain",
"work_email"
],
"limit": 25
}Rows from any of the three domains, mixed. On zero rows a no_rows warning names the keys that were applied.
Decision makers at one company
Find the people who decide at one named account, with a work email.
count_matchesPOST /contacts/count
{
"filter": {
"domains": [
"acme.example.com"
],
"seniority": [
"c_level",
"vp",
"director"
],
"has_work_email": true
}
}The number of leaders at the one domain, free. Add functions to keep only the buyer's department.
find_contactsPOST /contacts/search
{
"filter": {
"domains": [
"acme.example.com"
],
"seniority": [
"c_level",
"vp",
"director"
],
"has_work_email": true
},
"columns": [
"id",
"name",
"title",
"company_domain",
"seniority",
"work_email"
],
"limit": 10
}The leaders at that domain, one call for the whole account. On zero rows a no_rows warning names the keys that were applied.
Decision makers across many accounts
Give me up to three leaders at each of our target accounts, in one list.
count_matchesPOST /contacts/count
{
"filter": {
"domains": [
"acme.example.com",
"northwind.example.net",
"globex.example.org",
"initech.example.com"
],
"seniority": [
"c_level",
"vp",
"director"
],
"has_work_email": true
}
}One number over every domain, free. The whole list goes in one call, never one call for each domain. One call takes up to 1,000 domains.
find_contactsPOST /contacts/search
{
"filter": {
"domains": [
"acme.example.com",
"northwind.example.net",
"globex.example.org",
"initech.example.com"
],
"seniority": [
"c_level",
"vp",
"director"
],
"has_work_email": true
},
"columns": [
"id",
"name",
"title",
"company",
"company_domain",
"seniority"
],
"max_rows_per_company": 3,
"limit": 25
}A page with at most three rows for each company_domain, so no single account fills the list. start_export takes the same filter and cap to deliver a file.
The colleagues of a contact
I have one contact at an account: find the other leaders there who would also buy.
count_matchesPOST /contacts/count
{
"filter": {
"domains": [
"acme.example.com"
],
"seniority": [
"c_level",
"vp",
"director"
],
"functions": [
"sales_and_bizdev",
"marketing_and_advertising"
]
}
}The number of leaders at the contact's company_domain in the functions that buy the offer, free.
find_contactsPOST /contacts/search
{
"filter": {
"domains": [
"acme.example.com"
],
"seniority": [
"c_level",
"vp",
"director"
],
"functions": [
"sales_and_bizdev",
"marketing_and_advertising"
]
},
"exclude_ids": [
"per_01J9Z3K7Q8M2N4P6R8T0V2X4Y6"
],
"columns": [
"id",
"name",
"title",
"company_domain",
"seniority",
"function"
],
"limit": 10
}The leaders at that domain without the contact you started from. exclude_ids sits beside the filter and works on count, search and export.
Exclude a competitor and an industry
US directors and managers, but not at our competitor and not in banking.
get_valuesGET /contacts/schema/company_industries/values?q=bank&limit=5
The matching industry slugs with their labels, so the agent sends a slug the contract knows. Free.
count_matchesPOST /contacts/count
{
"filter": {
"countries": [
"us"
],
"seniority": [
"director",
"manager"
],
"exclude_domains": [
"competitor.example.com"
],
"exclude_industries": [
"banking_and_lending"
],
"exclude_functions": [
"human_resources"
]
}
}The count after the three exclusions. Each exclude_ key is the NOT of its twin.
find_contactsPOST /contacts/search
{
"filter": {
"countries": [
"us"
],
"seniority": [
"director",
"manager"
],
"exclude_domains": [
"competitor.example.com"
],
"exclude_industries": [
"banking_and_lending"
],
"exclude_functions": [
"human_resources"
]
},
"columns": [
"id",
"name",
"title",
"company_domain",
"seniority"
],
"limit": 10
}A page with no row from the excluded domain, industry or function.
A where clause with not_in
Engineering leaders in the United States without either of two named skills.
count_matchesPOST /contacts/count
{
"filter": {
"countries": [
"us"
],
"functions": [
"engineering"
],
"seniority": [
"c_level",
"vp",
"director"
],
"where": {
"field": "skills",
"op": "not_in",
"value": [
"recruiting",
"accounting"
]
}
}
}The count with the tree applied. There is no exclude_skills key: a negation outside the six flat ones goes through where with not_in.
find_contactsPOST /contacts/search
{
"filter": {
"countries": [
"us"
],
"functions": [
"engineering"
],
"seniority": [
"c_level",
"vp",
"director"
],
"where": {
"field": "skills",
"op": "not_in",
"value": [
"recruiting",
"accounting"
]
}
},
"columns": [
"id",
"name",
"title",
"company_domain",
"seniority"
],
"limit": 10
}Rows whose skills contain neither value. A tree over 32 predicates answers 422 filter.rejected.
A headcount band and a funding round
C-level people at US companies of 201 to 500 staff that raised a Series B.
get_valuesGET /contacts/schema/company_size/values?q=201&limit=10
The headcount bands that match, so the agent sends a band the contract knows rather than two numbers. The funding round slugs are a frozen list on the Filters page. Free.
count_matchesPOST /contacts/count
{
"filter": {
"countries": [
"us"
],
"seniority": [
"c_level"
],
"company_size": [
"201-500"
],
"last_round_types": [
"series_b"
]
}
}The count for the band and the round together. A company with no funding on record is null, never zero, so it does not match.
find_contactsPOST /contacts/search
{
"filter": {
"countries": [
"us"
],
"seniority": [
"c_level"
],
"company_size": [
"201-500"
],
"last_round_types": [
"series_b"
]
},
"columns": [
"id",
"name",
"title",
"company",
"company_domain"
],
"limit": 10
}A page, or an empty data with a no_rows warning that lists the four keys and asks for one to be removed.
A signal: a recent job change
US sales leaders who started a new job recently, because they buy in their first quarter.
count_matchesPOST /contacts/count
{
"filter": {
"countries": [
"us"
],
"functions": [
"sales_and_bizdev"
],
"where": {
"field": "signals",
"op": "in",
"value": [
"recent_job_change"
]
}
}
}The count for the signal. Signals have no flat key: they are filtered with in or not_in on signals inside where.
find_contactsPOST /contacts/search
{
"filter": {
"countries": [
"us"
],
"functions": [
"sales_and_bizdev"
],
"where": {
"field": "signals",
"op": "in",
"value": [
"recent_job_change"
]
}
},
"columns": [
"id",
"name",
"title",
"company_domain",
"signals",
"role_since"
],
"limit": 10
}Rows that carry the signal, with signals and role_since on the row so the agent can read the date itself.
At most two people per company
A software list that is not all from one account: at most two people per company.
count_matchesPOST /contacts/count
{
"filter": {
"industries": [
"software_development"
],
"seniority": [
"c_level",
"vp",
"director"
],
"has_work_email": true
}
}The count of everyone who matches. The count does not apply the per-company cap: the cap belongs to the page.
find_contactsPOST /contacts/search
{
"filter": {
"industries": [
"software_development"
],
"seniority": [
"c_level",
"vp",
"director"
],
"has_work_email": true
},
"columns": [
"id",
"name",
"title",
"company",
"company_domain"
],
"max_rows_per_company": 2,
"limit": 25
}A page holding at most two rows for each company_domain. The cap applies to each page, not to the whole search.
The second page, by cursor
Read a US audience page by page, and never show me the same person twice.
find_contactsPOST /contacts/search
{
"filter": {
"countries": [
"us"
],
"has_work_email": true
},
"columns": [
"id",
"name",
"title",
"company_domain",
"country"
],
"limit": 5
}Five rows and a page.next_cursor. The cursor is opaque and it pins the filter, the sort tuple and the build.
find_contactsPOST /contacts/search
{
"filter": {
"countries": [
"us"
],
"has_work_email": true
},
"columns": [
"id",
"name",
"title",
"company_domain",
"country"
],
"limit": 5
}The next five rows, with no id from the first page. Read pages until next_cursor is null. A cursor from an older build answers 400 cursor.expired.
An export that gets truncated
Write me a file of US contacts with a work email, but cap this job at 25 rows.
count_matchesPOST /contacts/count
{
"filter": {
"countries": [
"us"
],
"has_work_email": true
}
}The size of the audience, free, before any row is spent.
start_exportPOST /exports
{
"filter": {
"countries": [
"us"
],
"has_work_email": true
},
"columns": [
"id",
"name",
"work_email",
"company_domain",
"country",
"seniority"
],
"format": "csv",
"max_rows": 25
}202 with rows_available, rows_to_export: 25, truncated: true and truncation_reason: "max_rows", plus an export_truncated warning. The contact credits are held, not charged.
get_exportGET /exports/{export_id}
The job status. Poll until it is completed, then read url: a signed link on our own domain that lives 7 days from completion. An expired or gone file answers 410 with the code export.expired. The contact credits are charged on completion.
Enrichment by LinkedIn URL
Fill in the record for a LinkedIn profile I already have.
enrich_contactPOST /enrichments
{
"inputs": [
"https://www.linkedin.com/in/sample-dana-reyes-1ifdfs"
],
"columns": [
"id",
"name",
"title",
"company",
"company_domain",
"work_email",
"seniority"
]
}One result for each input, in the order they were sent, each with input, matched, off_domain and contact. A hit costs one contact credit and a miss costs nothing. v1 answers from our own store; refresh answers 400 enrich.mode_unavailable.
enrich_contactPOST /enrichments
{
"inputs": [
"https://www.linkedin.com/in/sample-dana-reyes-1ifdfs",
"dana.reyes@example.com"
],
"company_domain": "example.com",
"columns": [
"id",
"name",
"title",
"company",
"company_domain",
"work_email"
]
}The same call scoped to the account the people came from. A person we hold at another employer, or with no employer domain, answers matched: false with off_domain: true, costs nothing, and is listed on one off_domain warning. So a list sourced from one account never comes back with an email at a previous employer, an agency, or a school.
Docs for agents: llms.txt