PloidDocs
Contacts

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/enrich

Requires 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 200 that answers some fields while one is still running, the pending field has status: "unavailable" and meta.warnings carries an entry such as work_email is still being looked up; retry in about 30 seconds. The response carries Retry-After: 30.
  • When every requested field is unavailable the response is the retryable 503 upstream_unavailable with error.unavailable_fields — there is no data or meta.warnings — and Retry-After: 30 while a lookup is still running in the background (1 when 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.

ConditionStatus and code
LinkedIn URL or supported identity is missing422 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.