Contacts
Get the details you need to reach someone. Enrich any person with verified work and personal emails, phone numbers, and soon mailing addresses.
Contact resolution
POST /v1/enrichRequires people:enrich. Identify the person with exactly one of linkedin_url,
person_id, email, or name plus company_domain. An email combined
with first_name and last_name at a work domain searches for that name at
the company; a free-mail domain, or no names, does a reverse lookup for the
person behind the email.
curl https://api.ploid.com/v1/enrich \
-H "Authorization: Bearer $PLOID_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"linkedin_url": "https://www.linkedin.com/in/example",
"fields": ["work_email", "personal_email", "phone"]
}'Every requested field is returned, even when it is not found:
{
"data": {
"work_email": {
"value": "example@company.com",
"status": "found",
"confidence": "verified",
"source": "fullenrich",
"last_seen": "2026-08-31T00:00:00.000Z"
},
"phone": {
"value": null,
"status": "not_found",
"confidence": null,
"source": null,
"last_seen": null
}
},
"meta": {
"usage": {
"billed": ["work_email"],
"free": [],
"not_found": ["phone"]
}
}
}Generate a fresh Idempotency-Key for each logical request and reuse that key
only when retrying the same request body. The Ploid CLI and API playground do
this automatically.
status is unavailable for a provider or transport failure, so it is never
silently reported as a clean miss. Only successful reveals are billed.
The legacy enrichments lightweight LinkedIn/contact contract is deprecated.
Existing clients should migrate from enrichments to fields; it remains
available throughout API v1 and will only be removed in a future major API
version. It is the only contract that accepts response_format — set it to
markdown for a text/markdown response; the fields contract always
returns standard JSON. Deep public evidence lives only at /v1/person,
which provides durable runs and 90-day organization-wide rereads.
Partial success
One field can fail without failing the whole person. Unresolved fields return null, and transport or provider failures appear in meta.warnings. Clean not-found fields and failed fields are not charged.
If every requested field fails, the API returns 503 upstream_unavailable
with error.retryable: true and error.unavailable_fields. Retry-After is
30 when a field is still being looked up (see below) and 1 otherwise.
No fields are billed. A completed lookup that finds no contact remains a 200
response. The legacy enrichments contract fails the same way, except its
Retry-After is always 1.
Slow lookups
A provider lookup that is still running when the request's deadline passes does not get thrown away. It shows up in two shapes:
- On a
200that answers some fields while one is still running, the pending field hasstatus: "unavailable"andmeta.warningscarries an entry such aswork_email is still being looked up; retry in about 30 seconds.The response carriesRetry-After: 30. - When every requested field is unavailable the response is the retryable
503 upstream_unavailablewitherror.unavailable_fields— there is nodataormeta.warnings— andRetry-After: 30while a lookup is still running in the background (1when none is).
The lookup keeps polling the provider in the background until it answers (with
a 30-minute safety cap). When the person resolves to a LinkedIn profile — a
linkedin_url request, or a person_id or email lookup Ploid tied to one —
retrying with the same fields, or a subset of them, joins the running lookup or
is served from the result it stored, with no second provider lookup. For a
name plus company_domain lookup, or a person Ploid could not tie to a
LinkedIn profile, a retry can start a new lookup instead. Billing is unchanged:
only found fields are charged. The field statuses stay found, not_found,
and unavailable.
After a partial 200, retry with a fresh Idempotency-Key, ideally requesting
only the unavailable fields as the CLI does — a stored 200 is replayed for
24 hours, and a different body under the same key returns
409 idempotency_conflict. Retrying a 503 with the same key is safe.
Identity first
Use the strongest known identity anchor available. A canonical LinkedIn URL is safer than a name alone, especially for common names.
Errors and limits
Requested fields run concurrently. Provider lookups have an 80-second deadline;
if they exceed it, the API returns 503 upstream_timeout with retryable: true
instead of waiting for a gateway timeout. That attempt does not charge enrichment
credits. Retry the request using the same Idempotency-Key when one was supplied.
| Condition | Status and code |
|---|---|
| LinkedIn URL or supported identity is missing | 422 identity_required or 422 validation_failed |
Focused enrichment uses the organization and API-key per-minute buckets; it has no additional route cap. Partial provider failures remain in meta.warnings; complete provider failure returns a retryable 503. You can retry with the same Idempotency-Key after Retry-After. See Errors & rate limits.
