Find Contact Info
Buy records first, then enrich them with POST /enrichments — or turn on contact lookup inside one request. Paid plans and live trials.
Who this is for
Lead-gen teams who need a person to contact, not just a project to read about.
The rules, in one place
- Off unless you ask. Default search, detail, AI search and CSV stay unenriched.
- Paid plans and live trials. Unpaid keys get
402 enrichment_requires_paid_plan. A live trial spends the separate 5-lookup grant. Names, titles and companies from the filing still ship without the toggle. The website CSV Find Contact Info toggle stays on paid plans. - Enriching what you already bought is free.
POST /enrichmentstakes the project IDs you have already purchased, charges no credits, and draws on a monthly enrichment allowance instead. - No extra Boardwalk credit inside a request either. 50 records with Find Contact Info on still cost 50 credits, whether we found 10 people or none.
Enrich what you already bought — free
The flagship flow: pull the records that match your thesis, then enrich exactly the ones you kept. The pipeline runs about a minute per lead, so it is a job you poll, not a request you wait on.
# 1. Queue enrichment for purchased projects — free, 202 Accepted
# Idempotency-Key is REQUIRED: a retry replays the same job instead of
# submitting a second one (400 invalid_request without it).
POST /api/v1/enrichments
Idempotency-Key: enrich-sunbelt-2026-08-27
{ "projectIds": [10754108, 10754109, 10754110] }
# 2. Poll progress until status is completed (queued | running | completed | failed)
GET /api/v1/enrichments/57
# → { "status": "running", "requested": 100, "processed": 57, "found": 41, "pending": 43, ... }
# 3. Read the contacts back — by ID, so this is a re-delivery and not a new search
# An id selection suppresses the default date window, so a purchase with an
# older meeting date still comes back.
GET /api/v1/projects/search?projectIds=10754108,10754109,10754110&include=contacts&contactOptions={"cachedOnly":true}A job takes at most 250 project ids; a longer list is a 400 invalid_request on projectIds carrying details.maxProjectsPerJob. A full month's allowance is therefore more than one job — split it. IDs you never purchased are skipped and named under skipped.notPurchased — buy them through any billed route first. IDs already enriched recently are skipped free under skipped.alreadyEnriched. found counts the projects where contact info was found so far — the honest yield number. The monthly allowance (500 attempts on the API Only plan, calendar-month reset) is reported in three different shapes, so read the one the route you called actually returns: the submit response carries an allowance object (used / limit / remaining / resetsAt); the poll carries two flat counters, allowanceUsed and allowanceRemaining, and no allowance object at all; and GET /account/credits carries it under data.enrichment as monthlyAttemptsLimit / attemptsUsed / attemptsRemaining / resetsAt. A finished job settles the allowance to the projects it actually found somebody for, so a project that came back empty costs you nothing, and if the per-request ceilings stop a job early the poll body says so — notAttempted counts the projects never tried, they were not charged against the allowance, and resubmitting exactly those ids is free to try. Expect roughly a minute per lead at about two leads per project: a 100-project job is a multi-hour job (its processing deadline scales with the batch), and estimatedCompletion is a polling hint, not a deadline.
How to turn it on
Same feature as the website export toggle. Pick one path:
# CSV from the API — same job as the map Export button
GET /api/v1/projects/search?format=csv&include=contacts&states=UT
# JSON live lookup — cannot run inside a GET
POST /api/v1/projects/ai-search/jobs
{ "query": "multifamily continued for parking", "states": ["UT"], "limit": 50, "include": "contacts" }
# JSON, contacts we already hold — no new vendor lookup
GET /api/v1/projects/search?include=contacts&contactOptions[cachedOnly]=true&states=UTThe same search, with Find Contact Info switched on
curl -X POST 'https://api.boardwalkai.com/api/v1/projects/ai-search/jobs' \
-H 'Authorization: Bearer bwk_live_YOUR_KEY' \
-H 'Content-Type: application/json' \
-d '{
"query": "multifamily projects that were continued for parking concerns",
"states": [
"UT"
],
"limit": 50,
"include": "contacts",
"contactOptions": {
"minConfidence": "high"
}
}'Response · HTTP 202
{
"requestId": 918509,
"status": "queued",
"pollUrl": "https://api.boardwalkai.com/api/v1/projects/ai-search/jobs/918509",
"meta": {
"creditsReserved": 50,
"limit": 50,
"trialTruncation": null,
"requestId": "req_01K1QF3M0000EXAMPLE0001"
}
}The response shape on this page is the one the endpoint builds — the keys, their nesting and their types are asserted against the shipping code by a test. The numbers inside it are the scenario being walked through, not a measurement of the corpus.
A GET that asks for a live lookup returns 400 enrichment_requires_async and names the AI-search job and the CSV export. representation=compact cannot carry contacts; use standard or full.
Optional knobs
| Option | Values | Effect |
|---|---|---|
contactOptions.minConfidence | high, medium | The floor for a contact to ship. Defaults to medium. There is no low. |
contactOptions.cachedOnly | boolean | JSON only: return contacts we already hold. No new lookups. Not valid on format=csv. |
contactOptions.companyOnly | boolean | Company-level contacts only. |
contactOptions.maxProjects | positive integer | Cap how many of the returned projects are looked up. |
An unknown key or an unknown value is a 400 that costs nothing, before anything runs. Sending cachedonly instead of cachedOnly fails loudly rather than quietly running the most expensive path on the platform.
Reading the result
JSON records carry a contactEnrichment block whether or not you asked, so clients branch on its value rather than its existence:
contactEnrichment.status | Meaning |
|---|---|
not_requested | You did not ask. The contacts array still carries anyone named on the filing itself. |
pending | Requested, not finished. |
completed | We looked. contactsFound says what we found — including zero. |
failed | The lookup could not be completed. You were still charged 1 credit for the record, which you received. |
CSV files put the looked-up person on the contact columns (name, title, email, phone, LinkedIn). Staff names may appear; staff email and phone never do.
The rules that do not bend
- Government staff contact details are never returned, on any tier. Planners, clerks and commissioners appear in
publicOfficialswith their name, title and organisation, and no contact channel. - Suppressed contacts are removed everywhere. Anyone can ask us to stop distributing their contact details, and once suppressed they do not ship to anyone.
Next
Ready to make a call?
A free Boardwalk trial includes API access, 50 record-export credits (search, CSV, MCP), and 5 Find Contact Info lookups. Counting, taxonomy, location lookups and the analytics plane cost nothing, so you can evaluate the data before you spend a credit.