Guides
Contacts
POST /v1/contacts retrieves an email address, a phone number, or both for ONE person a search returned. It answers inside the request when it can, and hands you a job when the provider takes longer.
Ask for one person's contacts
The body names an opaque person_id from Search and the channels you want — email, phone, or both. The Idempotency-Key header is required: it is what makes the request safe to retry without paying twice.
curl --request POST "https://developer-api.seekout.io/v1/contacts" \
--header "Authorization: Bearer $SEEKOUT_API_KEY" \
--header "Content-Type: application/json" \
--header "Idempotency-Key: $(uuidgen)" \
--data '{
"person_id": "person_bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"channels": ["email", "phone"]
}'A 200 carries one entry per channel you asked for, each with its own status, values, lease expiry, and money fact. Channels succeed and fail independently: below, the email was found and charged and the phone was not found and cost nothing.
HTTP/1.1 200 OK
{
"operation_id": "01JEXAMPLEJOB0000000000000",
"person_id": "person_bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"status": "completed",
"charged_microunits": "1000",
"replayed": false,
"channels": {
"email": {
"status": "delivered",
"values": [{ "value": "someone@example.test", "type": "work" }],
"reused": false,
"lease_expires_at": "2026-09-16T12:00:00Z",
"charged_microunits": "1000",
"receipt_id": "rcpt_example"
},
"phone": {
"status": "not_found",
"values": [],
"reused": false,
"lease_expires_at": null,
"charged_microunits": "0",
"receipt_id": null
}
}
}Amounts in these samples are placeholders, not rates. Pricing is consumption-based, so you only pay for data you get back, and it is shown to signed-in accounts under Pricing in the console.
Channel statuses
| Status | Meaning | Charged |
|---|---|---|
delivered | Values were retrieved for this channel | Yes |
reused | Your organization already holds a live lease for this person and channel, so the values came back without new provider work | No |
not_found | The provider has no value for this channel | No |
failed | The provider could not answer for this channel | No |
pending | Still running. Only appears on a 202, never on a 200 | Not yet |
cancelled | The operation was cancelled before this channel resolved | No |
reused: true means you already paidA delivered contact leaves your organization a seven-day lease on that person and channel. Any key in the organization retrieving the same pair inside the window gets the values back with reused: true and charged_microunits: "0". Reading it again never extends the lease.When it takes longer than the request: 202 with a job
Retrieval is bounded synchronous. When the provider outlasts the request budget you get 202 with a Job instead of the contact shape, plus a Retry-After header. The job_id is the same operation_id the 200 would have carried.
HTTP/1.1 202 Accepted
Retry-After: 5
{
"job_id": "01JEXAMPLEJOB0000000000000",
"type": "contact_retrieval",
"status": "running",
"counts": { "requested": 2, "delivered": 0, "pending": 2 },
"reserved_microunits": "2000",
"charged_microunits": "0",
"result_available": false,
"result_url": "/v1/job-results/01JEXAMPLEJOB0000000000000"
}Poll GET /v1/jobs/{job_id} until the status is terminal, then read GET /v1/job-results/{job_id}. Or register a webhook and be told instead of asking. See contact jobs for both reads.
Retries and idempotency
- Reuse the SAME
Idempotency-Keyto retry: the request resumes the operation it already started and never charges a second time. - The reply says
replayed: truewhen it resumed rather than started. - Reusing a key with a DIFFERENT body is
409— generate a new key for genuinely different work. - A
202is not a failure. Do not retry it as one: read the job.
Failures worth handling
| Status | Code | What to do |
|---|---|---|
402 | insufficient_credits | Add credits. Raised before any provider work, so nothing was charged and no lease was created |
403 | insufficient_scope | The key needs the Contacts access set |
404 | person_not_found | The person_id is not one Search issued for your organization |
429 | — | Wait for Retry-After and retry with the same idempotency key |
503 | — | The contact provider is unavailable or an outcome is uncertain. Retry after Retry-After; nothing settles twice |
See credits for how a delivered channel is charged and errors for the problem-document shape.