Skip to main content
Enrichment pulls one known person’s own data from LinkedIn through a live browser session. This page covers the two methods that can return an email or a phone number, which of your accounts each one runs on, and how to read the receipt that comes back. A list of other people (search results, employees, post reactions) is scraping instead: a different surface.

Before you start

  • A connected LinkedIn account: every enrichment runs on one of your own accounts.
  • The can_act_linkedin_enrichment permission on your API key, agent or user.
  • One identifier: profile_id (ACoAA... or ACwAA...) or public_identifier (the vanity slug from /in/...). Exactly one, never both, or the call is 422 validation_failed.

The two methods that return contact details

Both ride the same read but cache under their own key: a cached full profile does not satisfy a contact-info call, and the reverse is also true.
The flat phone on person-full-profile keeps only the first mobile number. Home and work numbers are dropped. person-contact-info returns phones[] with a type on each entry (MOBILE, HOME, WORK, or null), so use it when the phone actually matters.

Run it

curl
No executor fields there: contact info is routed to whichever of your accounts is connected to the person. The call is synchronous, and the data comes back under result.contact_info, next to the ledger row at result.data_request (sid starting er_rq_, status, executed_on, served_from_cache, cached_from_sid). Per-method shapes are in the API reference, and batching is your own loop (no mass action, no sequence step), one target per call. contact_info is always present, and every field inside it can be empty. An empty block means the connected account still cannot see those details, never that the call was blocked. address, birthday and connection date are not parsed. A phone with type: null is normal on older cached payloads, not a defect.

Which account runs the call

Every enrichment runs on one of your own connected accounts.
There is no silent fallback for a pinned account. If the account you named is out of daily budget the call returns 429 rate_limited, reason bucket_saturated, with a retry_after timestamp. It will not quietly move to an account you did not name.

Contact info is the exception

person-contact-info requires a connected executor: LinkedIn reveals contact details to 1st-degree connections only, so the pick is narrowed to accounts that actually hold the edge. Here linkedin_account_sid is a preference, not a command: another connected account is picked if the one you named does not qualify, and accounts out of budget, on hold or LinkedIn rate-limited are dropped before the pick.

How long a result is cached

A repeat for the same person and method inside the TTL is served from the stored result (served_from_cache: true, nothing dispatched); past it, the call executes again. Cache is per team, per method.

How to read a partial result

An empty answer is a real answer. A person who never published a phone number produces a completed row with an empty field, not an error. A row is inserted either running or already terminal; running then moves to completed, failed or rejected (a reservation failure rejects an already running row). A call has a hard deadline of 90 seconds; typical latency is 30 seconds at p95 executed, 500 ms at p95 cache-served. Retrying is safe when you reuse the idempotency_key; inside that window the replay returns the running row, so poll GET /api/data-requests/{sid} until the status is terminal. To follow results without polling, subscribe to the data-requests.completed and data-requests.failed webhooks; the completed payload adds result_ref and completed_at to the receipt fields. Refusals have no event: a rejection appears only as the synchronous HTTP error.

Daily budget

Enrichment spends one shared bucket per account called enrichment. Read it before a batch, on the Smart Limits tab of the account drawer or through smart limits and warmup filtered on limit_type.
Default budget: 80 enrichment actions per day per account, with a 240 second gap enforced between counted actions. When the budget is spent or the gap has not elapsed the call returns 429 rate_limited, reason bucket_saturated, with a retry_after value; retry after that timestamp, not on a fixed schedule. The free plan clamps this to 12 enrichments per day per connected account.

Errors you will actually hit

bucket_saturated on a 429 is your own account’s daily enrichment budget, nothing wider. Wait for retry_after, or pin a different connected account that still has budget.
Copy this to your AI assistant to enrich a list of profiles:“Enrich LinkedIn profile slugs with contact details using the GTM API at https://app.gtm-api.com/linkedin/v4. For each slug POST to /api/linkedin-enrichment/person-contact-info with body {"public_identifier": SLUG, "idempotency_key": "contact-SLUG"} and header Authorization: Bearer gtm_live_YOUR_KEY. Do not send linkedin_account_sid: this method picks my connected account on its own. One profile per call, sequentially. Treat 422 no_connected_account as skip-and-log, and on 429 wait until the retry_after timestamp. Write CSV: slug, email, first phone, phone type, websites, data_request.status.”