LinkedIn Profile Details
Enrich any LinkedIn profile URL. Headline, experience, education. Clean JSON in one call.
Run as an Apify Actor. Submit up to 5,000 URLs per run, pay only for profiles found.
Quickstart (Apify API)
# Trigger the actor with the Apify APIcurl -X POST "https://api.apify.com/v2/acts/atomus~linkedin-profile-scraper/runs?token=YOUR_APIFY_TOKEN" \ -H "Content-Type: application/json" \ -d '{ }'What it does
Send up to 100 public LinkedIn profile URLs per call. Get back clean, normalized JSON with name, title, headline, summary, company, location, and skills. Each URL is billed at one credit; per-URL errors are refunded automatically. No LinkedIn account required on your end.
Endpoint
POST https://api.atomusapi.dev/v1/linkedin-profile-enrichment/enrichAuthentication
Send your API key as a Bearer token in the Authorization header. Keys are scoped to a single tenant.
Authorization: Bearer atm_live_xxxxxxxxxxxxxxIdempotency
Send Idempotency-Key: <uuid> to make a request safely retryable. Atomus stores the response for 24h and replays it on retries with the same key.
Input
Request body
| Field | Type | Description | |
|---|---|---|---|
| url | string | required | A single LinkedIn profile URL to enrich. Must match https://www.linkedin.com/in/<handle>/. To enrich multiple profiles, send one request per URL; the rate limit (4 req/s) lets you parallelize on the client side. |
Output
Response object
| Field | Type | Description | |
|---|---|---|---|
| data.url | string | required | Echoes the input URL. |
| data.status | string | required | One of: success, not_found, error. |
| data.profile.full_name | string | null | optional | Full display name. Present only when status = success. |
| data.profile.title | string | null | optional | Current job title. success only. |
| data.profile.headline | string | null | optional | Profile headline (the short tagline below the name on LinkedIn). success only. |
| data.profile.summary | string | null | optional | Profile summary / about section. success only. |
| data.profile.company.name | string | null | optional | Current company name. success only. |
| data.profile.location.country | string | optional | Country code (ISO 3166-1 alpha-2). success only. |
| data.profile.location.state | string | optional | State or region. success only. |
| data.profile.location.city | string | optional | City. success only. |
| data.profile.skills | string[] | optional | Listed skills. success only. |
| data.profile.educations | object[] | optional | Education history; opaque upstream shape, treat each entry as unknown for now. success only. |
| data.profile.position_groups | object[] | optional | Work history grouped by company; opaque upstream shape, treat each entry as unknown for now. success only. |
| data.code | string | optional | Error code. Present only when status = error. |
| data.message | string | optional | Error message. Present only when status = error. |
| meta.request_id | string | required | Opaque trace ID. Include when filing a support ticket. |
| meta.api | string | required | API identifier: linkedin-profile-enrichment. |
| meta.version | string | required | API version string. |
| meta.mode | string | required | Environment mode the request ran in. |
| meta.credits_used | number | required | Credits charged for this request (1 only when status=success; 0 for not_found and error — both reserve and refund). |
| meta.credits_remaining | number | required | Tenant credit balance after this request. |
| meta.duration_ms | number | required | Server-side processing duration in milliseconds. |
Example request
curl
curl -X POST "https://api.atomusapi.dev/v1/linkedin-profile-enrichment/enrich" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://www.linkedin.com/in/example-1" }'JavaScript
const res = await fetch( "https://api.atomusapi.dev/v1/linkedin-profile-enrichment/enrich", { method: "POST", headers: { "Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ url: "https://www.linkedin.com/in/example-1", }), });
if (!res.ok) { const { error } = await res.json(); throw new Error(`${error.code}: ${error.message}`);}
const { data, meta } = await res.json();if (data.status === "success") console.log(data.profile.full_name);else if (data.status === "not_found") console.log("profile not found");else console.error(data.code, data.message);Python
import requests
res = requests.post( "https://api.atomusapi.dev/v1/linkedin-profile-enrichment/enrich", headers={ "Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json", }, json={"url": "https://www.linkedin.com/in/example-1"}, timeout=10,)
if not res.ok: err = res.json()["error"] raise RuntimeError(f"{err['code']}: {err['message']}")
data = res.json()["data"]if data["status"] == "success": print(data["profile"]["full_name"])elif data["status"] == "not_found": print("profile not found")else: print(data["code"], data["message"])Errors & limits
{ "error": { "code": "rate_limit_exceeded", "message": "Human-readable explanation.", "retryable": true, "request_id": "req_01HXXX..." }}unauthenticatedHTTP 401 : Missing or malformed Authorization header. (do not retry)invalid_api_keyHTTP 401 : API key is not recognized or has been revoked. (do not retry)insufficient_creditsHTTP 402 : Tenant credit balance is below the request cost. Top up and resend. (do not retry)invalid_inputHTTP 400 : Request body is not valid JSON, or the url field is missing. (do not retry)validation_failedHTTP 422 : The url is not a valid linkedin.com/in/<handle> profile URL. (do not retry)rate_limit_exceededHTTP 429 : Sustained request rate exceeded the tenant limit. (retryable)upstream_errorHTTP 502 : Upstream provider returned a non-success response. (retryable)upstream_timeoutHTTP 504 : Upstream provider did not respond within the time budget. (retryable)internal_errorHTTP 500 : Unexpected server error. (retryable)
Rate limits
4 requests/second per account (shared across all API keys on the same account). Each request enriches one LinkedIn profile URL. To enrich many, parallelize requests on the client side up to the rate limit. Bursts above the limit return 429 rate_limit_exceeded; back off using the response headers. The per-second window is enforced server-side; the per-minute, per-hour, per-day, per-week, and per-month rows are sustained-throughput derivations.
| Window | Max requests |
|---|---|
| Per second | 4 |
| Per minute | 240 |
| Per hour | 14,400 |
| Per day | 345,600 |
| Per week | 2,419,200 |
| Per month | 10,368,000 |
x-ratelimit-limit: max requests per second for the account (shared across all keys)x-ratelimit-remaining: requests remaining in the current windowx-ratelimit-reset: unix epoch (seconds) when the window resets
Billing
- 1 credit per successful enrichment
Only success responses are billed. not_found (no profile found at the URL) and error responses are not charged — the credit is reserved up front and automatically refunded after the call. Each request reports the spend in meta.credits_used and the new balance in meta.credits_remaining.
Running high volumes?
Skip the marketplace tiers and get unit pricing scoped to your real call volume, with a direct line to the team that runs the pipeline.
Get volume pricing