{
  "data": {
    "id": "XT5kRtsthqyw9SACF",
    "actId": "Zdv4gdFl8M4KbpeD4",
    "userId": "6ELMCqk84xJAHu6Jd",
    "startedAt": "2026-10-15T18:59:12.398Z",
    "finishedAt": "2026-10-15T18:59:47.333Z",
    "status": "SUCCEEDED",
    "meta": {
      "origin": "CLI"
    },
    "stats": {
      "durationMillis": 34855,
      "runTimeSecs": 34.855,
      "computeUnits": 0.03872777777777778,
      "imageSizeBytes": 595162112
    },
    "options": {
      "useCache": true,
      "betaPackages": false,
      "memoryMbytes": 4096,
      "diskMbytes": 8192
    },
    "exitCode": 0,
    "inputSchema": "{\n  \"title\": \"LinkedIn Profile Scraper Without Login: Bulk URLs + Work Email input\",\n  \"type\": \"object\",\n  \"schemaVersion\": 1,\n  \"properties\": {\n    \"profileUrls\": {\n      \"title\": \"LinkedIn profiles to look up\",\n      \"type\": \"array\",\n      \"description\": \"One profile per line. Paste the full profile link, or just the part after /in/. Accepted formats: https://www.linkedin.com/in/williamhgates, linkedin.com/in/williamhgates, williamhgates. You're only charged for the ones we actually find — a miss costs nothing.\",\n      \"editor\": \"stringList\",\n      \"prefill\": [\n        \"https://www.linkedin.com/in/williamhgates\",\n        \"satyanadella\"\n      ]\n    },\n    \"testRun\": {\n      \"title\": \"Try it first — only process the first 5\",\n      \"type\": \"boolean\",\n      \"description\": \"Turn this on to test your input on a small sample before running the full list. Turn it off to process everything.\",\n      \"editor\": \"checkbox\",\n      \"default\": false\n    },\n    \"onlyFound\": {\n      \"title\": \"Hide rows with no result\",\n      \"type\": \"boolean\",\n      \"description\": \"Only keep rows where something was actually found. Misses are always free, whether or not you show them here.\",\n      \"editor\": \"checkbox\",\n      \"default\": false,\n      \"sectionCaption\": \"🔎 Filter results\"\n    },\n    \"includeKeywords\": {\n      \"title\": \"Only keep rows containing\",\n      \"type\": \"array\",\n      \"description\": \"Optional. Only keep results that mention at least one of these words (e.g. a job title, a city, a product name). Leave empty to keep everything.\",\n      \"editor\": \"stringList\",\n      \"prefill\": []\n    },\n    \"excludeKeywords\": {\n      \"title\": \"Skip rows containing\",\n      \"type\": \"array\",\n      \"description\": \"Optional. Drop any result that mentions one of these words. Leave empty to skip nothing.\",\n      \"editor\": \"stringList\",\n      \"prefill\": []\n    },\n    \"maxResults\": {\n      \"title\": \"Stop after this many results\",\n      \"type\": \"integer\",\n      \"description\": \"Optional. Stop the run once this many results have been found — useful for a quick, cheap sample. Leave blank for no limit.\",\n      \"editor\": \"number\",\n      \"minimum\": 1,\n      \"nullable\": true\n    },\n    \"deltaMode\": {\n      \"title\": \"Flag job changes since the last run\",\n      \"type\": \"boolean\",\n      \"description\": \"Turns a scheduled list into a job-change monitor. Each profile's current company, job title and location are remembered between runs; every row still comes back, with 'Job change since last run', 'What changed', 'Previous company' and 'Previous job title' filled in, so you filter on the change. The first run records everyone as first-seen. A field LinkedIn hides on a given run is never reported as a change. Billing is unchanged: one charge per profile found.\",\n      \"editor\": \"checkbox\",\n      \"default\": false,\n      \"sectionCaption\": \"🔔 Monitoring\"\n    },\n    \"deltaName\": {\n      \"title\": \"Watchlist name (optional)\",\n      \"type\": \"string\",\n      \"description\": \"Leave empty to use one shared memory for this account. Type a name (for example 'sales-accounts' or 'candidates-q4') to keep two schedules of this actor from overwriting each other's memory of the same person.\",\n      \"editor\": \"textfield\",\n      \"default\": \"\"\n    },\n    \"flatten\": {\n      \"title\": \"Spreadsheet columns (one row = one line)\",\n      \"type\": \"boolean\",\n      \"description\": \"Adds the first three roles and the first school as plain text columns (Role 1: company, Role 1: title, Role 1: start, and so on), so a Google Sheets, Clay, Excel or n8n export needs no JSON parsing. The nested 'Current roles listed' and 'Schools listed' columns stay as they are. Pick the 'Spreadsheet' view in the Output tab for just those columns.\",\n      \"editor\": \"checkbox\",\n      \"default\": false,\n      \"sectionCaption\": \"📊 Spreadsheet export\"\n    },\n    \"findEmail\": {\n      \"title\": \"Guess each person's work email\",\n      \"type\": \"boolean\",\n      \"description\": \"Reads the current employer's public LinkedIn company page for its website domain (once per company per run), checks that the domain can receive mail, and fills 'Email guess' with the most likely address plus 2 alternatives. Guessed from the company's email pattern, not verified at the mailbox. No extra charge: still one charge per profile found. Adds one company-page request per distinct employer.\",\n      \"editor\": \"checkbox\",\n      \"default\": false,\n      \"sectionCaption\": \"📧 Email\"\n    },\n    \"columns\": {\n      \"title\": \"Which columns do you want?\",\n      \"type\": \"array\",\n      \"description\": \"Choose which pieces of information to include in each result row. All are included by default.\",\n      \"editor\": \"select\",\n      \"items\": {\n        \"type\": \"string\",\n        \"enum\": [\n          \"profileUrl\",\n          \"publicId\",\n          \"fullName\",\n          \"headline\",\n          \"location\",\n          \"currentTitle\",\n          \"currentCompany\",\n          \"currentCompanyUrl\",\n          \"experience\",\n          \"education\",\n          \"followers\",\n          \"connections\",\n          \"profileImageUrl\",\n          \"source\",\n          \"masked\",\n          \"changed\",\n          \"changeType\",\n          \"previousCompany\",\n          \"previousTitle\",\n          \"monitorFirstSeenAt\",\n          \"experience1Company\",\n          \"experience1Title\",\n          \"experience1Start\",\n          \"experience2Company\",\n          \"experience2Title\",\n          \"experience2Start\",\n          \"experience3Company\",\n          \"experience3Title\",\n          \"experience3Start\",\n          \"education1School\",\n          \"education1Degree\",\n          \"companyDomain\",\n          \"emailGuess\",\n          \"emailCandidates\",\n          \"emailDomainStatus\",\n          \"mxProvider\",\n          \"mailboxVerified\"\n        ],\n        \"enumTitles\": [\n          \"Profile link\",\n          \"Public profile ID\",\n          \"Name\",\n          \"Headline\",\n          \"Location\",\n          \"Current job title\",\n          \"Current company\",\n          \"Current company page\",\n          \"Current roles listed\",\n          \"Schools listed\",\n          \"Followers\",\n          \"Connections\",\n          \"Profile photo\",\n          \"Read from\",\n          \"LinkedIn hid part of this page\",\n          \"Job change since last run\",\n          \"What changed\",\n          \"Previous company\",\n          \"Previous job title\",\n          \"Monitored since\",\n          \"Role 1: company\",\n          \"Role 1: title\",\n          \"Role 1: start\",\n          \"Role 2: company\",\n          \"Role 2: title\",\n          \"Role 2: start\",\n          \"Role 3: company\",\n          \"Role 3: title\",\n          \"Role 3: start\",\n          \"School 1\",\n          \"School 1: degree\",\n          \"Company domain\",\n          \"Email guess\",\n          \"Other likely addresses\",\n          \"Email domain status\",\n          \"Mail provider\",\n          \"Mailbox verified\"\n        ]\n      },\n      \"default\": [\n        \"profileUrl\",\n        \"publicId\",\n        \"fullName\",\n        \"headline\",\n        \"location\",\n        \"currentTitle\",\n        \"currentCompany\",\n        \"currentCompanyUrl\",\n        \"experience\",\n        \"education\",\n        \"followers\",\n        \"connections\",\n        \"profileImageUrl\",\n        \"source\",\n        \"masked\",\n        \"changed\",\n        \"changeType\",\n        \"previousCompany\",\n        \"previousTitle\",\n        \"monitorFirstSeenAt\",\n        \"experience1Company\",\n        \"experience1Title\",\n        \"experience1Start\",\n        \"experience2Company\",\n        \"experience2Title\",\n        \"experience2Start\",\n        \"experience3Company\",\n        \"experience3Title\",\n        \"experience3Start\",\n        \"education1School\",\n        \"education1Degree\",\n        \"companyDomain\",\n        \"emailGuess\",\n        \"emailCandidates\",\n        \"emailDomainStatus\",\n        \"mxProvider\",\n        \"mailboxVerified\"\n      ],\n      \"sectionCaption\": \"📄 Columns & format\"\n    },\n    \"maxConcurrency\": {\n      \"title\": \"Max concurrency\",\n      \"type\": \"integer\",\n      \"description\": \"Parallel requests. Keep conservative — this target has no browser fallback, so getting blocked costs more than slow-and-steady.\",\n      \"default\": 2,\n      \"minimum\": 1,\n      \"maximum\": 8,\n      \"sectionCaption\": \"⚙️ Advanced — you can ignore this\"\n    },\n    \"proxyConfiguration\": {\n      \"title\": \"Proxy configuration\",\n      \"type\": \"object\",\n      \"description\": \"Apify Proxy config. Residential recommended for anti-bot-sensitive targets.\",\n      \"editor\": \"proxy\",\n      \"default\": {\n        \"useApifyProxy\": true,\n        \"apifyProxyGroups\": [\n          \"RESIDENTIAL\"\n        ]\n      }\n    }\n  },\n  \"required\": [\n    \"profileUrls\"\n  ]\n}",
    "readme": "# LinkedIn Profile Lookup: LinkedIn Profile Scraper, No Cookies\n\n**Paste a list of LinkedIn profile URLs and get back a spreadsheet: who each person is, where they work, and what their job title is today.**\n\n## Who uses this\n\n- **Sales and SDR teams** with a column of LinkedIn URLs in the CRM, who need name, title and company filled in before writing the first line of an email.\n- **Recruiters** checking whether a shortlist of 200 candidates still holds the job their notes say they do.\n- **RevOps and data teams** piping rows into Google Sheets, Clay or n8n so a lead record is complete the moment it lands.\n\n## Try it in 30 seconds\n\nThe input box is already filled in with two real public profiles. Click **Start** and you get one row per person, with the columns below. No login, no cookies, no `li_at` session token to paste in.\n\n## Example output\n\nReal rows from a recent run:\n\n| fullName | currentTitle | currentCompany | location | followers |\n|---|---|---|---|---|\n| Bill Gates | Co-chair | Gates Foundation | Seattle, Washington, United States | 40,689,855 |\n| Reid Hoffman | Co-Founder, Executive Board Chair | Manas AI | United States | 2,794,979 |\n| Satya Nadella | Chairman and CEO | Microsoft | Redmond, Washington, United States | 12,195,120 |\n\nEvery row also carries job history with start and end years, schools, connection count, the profile photo, and an optional work email guess.\n\n## What it costs\n\nAbout **$4 per 1,000 profiles found**. A profile that can't be read is free, so a list with dead or renamed URLs costs you nothing, and first runs fit inside Apify's free credit.\n\nGive this actor a list of public LinkedIn profile links and it fetches each one the way a\nsigned-out browser does, then hands you a flat row per person: name, headline, location, current\njob title and company, the current roles the page lists with their start and end years, schools\nwith years, follower and connection counts, and the profile photo. No login. No cookies. No\n`li_at` session token to paste in, no account of yours at risk, and nothing read from behind\nLinkedIn's sign-in wall.\n\nThe data comes from the `schema.org` Person block LinkedIn itself publishes in the page source of\nevery public profile, so a row is the profile's own structured description of itself rather than a\nguess assembled from scraped text. When LinkedIn decides a request should sign in first, the actor\nsays so in the row's `status` and charges you nothing.\n\n## Who it's for\n\nRecruiters and sourcers who already have a shortlist of profile links and need them as a table\ninstead of forty browser tabs. Sales teams who capture a LinkedIn URL on a form or in a CRM field\nand want the name, title, company and location filled in behind it before the first email goes out.\nFounders checking that an inbound lead's title still matches what their CRM said six months ago.\nAnd anyone building an agent or a workflow that has a profile link and needs the person behind it\nin a shape a script can read, without keeping a logged-in LinkedIn session alive somewhere.\n\nIf you do not have the profile links yet, this is the wrong tool: it looks up profiles you already\nname, it does not search LinkedIn for people matching a description.\n\n## Why this one\n\n- **No cookies, no session token, no account.** Every request is the plain public page. You are\n  never asked to hand over a `li_at` cookie, and no LinkedIn account of yours can be restricted\n  because of a run here.\n- **Read from LinkedIn's own structured data.** The row is built from the `schema.org` Person\n  block in the page source, not from CSS selectors chasing a design that changes monthly. Field\n  names stay put, which is what an agent or a Clay column needs.\n- **It tells you when the page was reduced.** LinkedIn can answer with a trimmed version of the\n  profile in which job titles and past employers are replaced by rows of asterisks. Those never\n  reach your table: the fields come back empty and the row is flagged `masked: true`, so you can\n  filter or re-run instead of importing placeholder text into a CRM. See the note under \"What you\n  get\" for how often that happens, measured rather than guessed.\n- **A block is a labelled miss, not a silent hole.** If LinkedIn asks the request to sign in, the\n  row says `status: BLOCKED` with a plain explanation and costs nothing. A profile that no longer\n  exists says `NOT_FOUND`. A company page pasted by mistake says `BAD_FORMAT`.\n- **Never charged for a miss.** Billing fires only on a row where a real profile came back.\n- **Residential proxy by default.** LinkedIn's sign-in wall is driven by the reputation of the IP\n  asking, so the actor ships with a residential proxy already selected, and retries a blocked\n  request on a fresh address rather than handing you the failure.\n\n## What you get\n\nOne row per profile you submit. Every row starts with the same five fields, then adds the profile\nfields you selected in \"Which columns do you want?\" (all of them by default).\n\n| Field | Type | Description |\n|---|---|---|\n| `query` | text | The profile you submitted, echoed back |\n| `found` | boolean | `true` when a public profile was read |\n| `status` | text | `OK` on a hit, or `NOT_FOUND`, `BLOCKED`, `BAD_FORMAT`, `REQUEST_FAILED` on a miss |\n| `message` | text | Plain-English reason for a miss, present only when `found` is `false` |\n| `scrapedAt` | ISO 8601 datetime | When the page was fetched |\n| `profileUrl` | link | Canonical profile link, `https://www.linkedin.com/in/<public id>` |\n| `publicId` | text | The part of the link after `/in/`, LinkedIn's stable public identifier |\n| `fullName` | text | Name as the public page shows it |\n| `headline` | text | The one-line headline under the name |\n| `location` | text | Location as shown, e.g. `Seattle, Washington, United States` |\n| `currentTitle` | text | Job title of the first current role listed |\n| `currentCompany` | text | Employer for that role |\n| `currentCompanyUrl` | link | That employer's LinkedIn company page |\n| `experience` | array | Current roles the public page lists, each `{title, company, companyUrl, location, startDate, endDate}` |\n| `education` | array | Schools the public page lists, each `{school, degree, startDate, endDate}` |\n| `followers` | number | Exact follower count, taken from the page's structured data rather than the rounded \"41M\" the top card prints |\n| `connections` | text | Connection count as LinkedIn displays it, e.g. `500+`, which is why it is text and not a number |\n| `profileImageUrl` | image | Profile photo on LinkedIn's media CDN |\n| `source` | text | `jsonld` when the row came from the page's structured-data block, `html` when it was assembled from the top card instead |\n| `masked` | boolean | `true` when LinkedIn served the reduced page and hid some titles or employers |\n| `changed` | boolean | Job-change monitor only: `true` when the company, title or location differs from the previous run; `null` when monitoring is off |\n| `changeType` | text | Job-change monitor only: `company`, `title`, `location`, `none` or `first-seen`; `null` when monitoring is off |\n| `previousCompany` | text | Job-change monitor only: the company the previous run remembered |\n| `previousTitle` | text | Job-change monitor only: the job title the previous run remembered |\n| `monitorFirstSeenAt` | ISO 8601 datetime | Job-change monitor only: when this watchlist first looked the profile up |\n| `experience1Company` ... `experience3Start` | text | Spreadsheet columns only: company, title and start of the first three listed roles, as plain text |\n| `education1School`, `education1Degree` | text | Spreadsheet columns only: the first listed school, as plain text (degree is null, see below) |\n| `companyDomain` | text | Email guess only: the current employer's website domain, from its public LinkedIn company page |\n| `emailGuess` | text | Email guess only: the most likely work address, guessed from the company's email pattern, not verified at the mailbox |\n| `emailCandidates` | array | Email guess only: the top 3 addresses, each `{pattern, email, confidence}` |\n| `emailDomainStatus` | text | Email guess only: `invalid` (no mail route), `catch-all` (disposable-mail domain) or `unknown` (real mail route, mailbox not checkable). Never `valid` |\n| `mxProvider` | text | Email guess only: `google`, `microsoft` or `other` |\n| `mailboxVerified` | boolean | Email guess only: always `false`, because no mailbox is ever contacted |\n\nThree honest limits on that table, stated here rather than left for you to discover in the data.\n`education[].degree` is null, because the logged-out page does not publish degree or field of study\nat all. `experience` covers the roles the public page presents as current; full career history is\nshown only to signed-in members and is not fetched. And there is no skills column, because no\nSkills section appeared in any of the fifteen public profile fetches this actor was built and\ntested against, so offering one would mean shipping a column that is always empty.\n\nThe other caveat is `masked`. LinkedIn decides per request whether to serve the complete profile\npage or a reduced one, and how often it reduces has changed over time. On 2026-09-08, the day this\nactor was built, the complete page came back on only four of fifteen fetches. From 2026-09-09 on\nit stopped reducing: 0 of 228 rows this actor returned through 2026-09-16 were masked, and a\ndedicated test on 2026-09-16 got the complete page on all 67 residential requests across ten\nprofiles. On a reduced page the name, location, current employer, follower and connection counts,\nschools and photo still come through; the headline and job titles do not, and the row is flagged\n`masked: true` so you can see exactly which rows those are. Today you should expect headline and\n`currentTitle` on nearly every row, but LinkedIn can switch back without notice, so filter on\n`masked` rather than assuming it never happens.\n\n## Run it on a schedule\n\nTurn a one-off list into a standing watchlist: instead of a fresh snapshot every time, each\nscheduled run tells you which profiles changed company, title or location since the last time you\nlooked.\n\nSet **LinkedIn profiles to look up** (`profileUrls`), turn on **Flag job changes since the last\nrun** (`deltaMode: true`), and optionally give it a **Watchlist name** (`deltaName`) so a second\nschedule doesn't share its memory.\n\nSchedule it in Apify (Schedules > Create) or trigger it from n8n/Make; the delta state lives in a\nnamed key-value store, so every scheduled run reads back exactly where the last one left off. See\n\"Track job changes\" below for the full mechanics.\n\n## Pricing\n\n<!-- GENERATED:PRICE:START -->\n$4 per 1,000 profiles, plus a $0.00005 start fee. Misses (`found:false`) are never charged.\n<!-- GENERATED:PRICE:END -->\n\nA run of 1,000 profiles costs about **$4** if every one comes back, less when some are blocked or\ngone, and less again on a paid Apify plan. Compare that with a per-seat enrichment platform billing\n$0.08 to $0.40 per enriched row on top of a licence, or with LinkedIn profile actors listed at\n$100 per 1,000. Rows that come back `found: false`, for any reason, are free.\n\n## Track job changes\n\nKnow the week a candidate, customer champion or prospect moves to a new company.\n\n1. Put the profiles you care about in **LinkedIn profiles to look up**, one per line.\n2. Turn on **Flag job changes since the last run**. Optionally type a **Watchlist name** such as `champions` so a second list never shares its memory.\n3. Save the input as a task and add a **Schedule**, weekly is enough for most teams.\n4. Add a webhook on **Run succeeded** that triggers an n8n or Make workflow, or posts to Slack. The workflow reads the run's dataset, keeps rows where `changed` is `true`, and sends `fullName`, `previousCompany`, `currentCompany` and `profileUrl`.\n\nThe first run marks everyone `first-seen`. After that each row says `company`, `title`, `location` or `none`. Every profile still comes back, so the same dataset can refresh a Google Sheet in full. LinkedIn sometimes hides job titles on the public page; a hidden value is never reported as a change, so you do not get false alarms. Billing does not change: one charge per profile found.\n\n## Spreadsheet export\n\nGet one clean line per person for Google Sheets, Excel, Clay or an n8n table, with no JSON to unpack.\n\nTurn on **Spreadsheet columns (one row = one line)**. Each row then also carries `experience1Company`, `experience1Title` and `experience1Start`, the same for roles 2 and 3, plus `education1School` and `education1Degree`, all as plain text. In the Output tab pick the **Spreadsheet** view, then export as CSV or Excel, or use Apify's Google Sheets integration to append each run to a sheet.\n\nThe nested `experience` and `education` columns stay available for tools that prefer them. Roles are the ones the public page lists as current, in the order LinkedIn shows them, and `education1Degree` is empty because the logged-out page does not publish degrees. If a saved task lists its own columns, the spreadsheet columns are added anyway once you tick the box. Combine it with the job-change monitor and a weekly schedule to keep a live contact sheet.\n\n## Guess work emails\n\nTurn a LinkedIn profile into a likely work email address, with no extra charge per row.\n\nTurn on **Guess each person's work email**. For every profile, the actor reads the current employer's public LinkedIn company page for its website, keeps the domain (`gatesfoundation.org`), checks that the domain can receive mail, and fills `emailGuess` with the most common company pattern (`bill.gates@gatesfoundation.org`) plus two alternatives in `emailCandidates`. Each company page is read once per run, however many of your profiles work there.\n\nEvery address is **guessed from the company's email pattern, not verified at the mailbox**. No mail server is contacted, so `mailboxVerified` is always `false` and `emailDomainStatus` is never `valid`: `unknown` means the domain has a real mail route, `invalid` means nothing can be delivered there (and no guess is given), `catch-all` means a disposable-mail domain. Confirm an address with a verification tool before you send to it at volume. A current employer that is a school, or a company page with no website or only a social link as its website, leaves the email columns empty. In a test on 2026-09-16, 25 of 25 company pages published a website, and all 25 domains had a mail route; all 6 school pages were blocked. Billing does not change: one charge per profile found.\n\n## Use it from Clay, n8n, Make, or an AI agent\n\n<!-- GENERATED:INTEGRATIONS:START -->\nThis actor runs synchronously over plain HTTP — call it directly from a script, a workflow tool, or an AI agent, no Apify Console needed once you have an API token.\n\n```bash\ncurl \"https://api.apify.com/v2/acts/accountable_eel~linkedin-profile-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>\" \\\n  -X POST \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"profileUrls\":[\"https://www.linkedin.com/in/williamhgates\",\"satyanadella\"]}'\n```\n\n**n8n.** Add an HTTP Request node: Method `POST`, URL `https://api.apify.com/v2/acts/accountable_eel~linkedin-profile-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>`, Body Content Type `JSON`, JSON Body `{\"profileUrls\":[\"https://www.linkedin.com/in/williamhgates\",\"satyanadella\"]}` (swap in an expression from an earlier node for a real value).\n\n**Clay.** Add an \"HTTP API\" column: Method `POST`, URL `https://api.apify.com/v2/acts/accountable_eel~linkedin-profile-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>`, Body `{\"profileUrls\":[\"{{profile}}\"]}`, mapping the row's profile into the `profileUrls` array.\n\n**MCP.** In Claude, Cursor, or any MCP client with the Apify MCP server, ask for \"LinkedIn Profile API & Scraper Without Login or Cookies\" — the agent will find and run this actor.\n<!-- GENERATED:INTEGRATIONS:END -->\n\n## How to use\n\n<!-- GENERATED:HOWTO:START -->\n1. **In the Apify Console.** Open the actor page and click **Start** — the `profileUrls` field is already pre-filled with a working example. Results land in the run's dataset as soon as each item is found.\n2. **Via the API.** Call it directly with a POST request — no Console needed once you have an API token:\n   ```bash\n   curl \"https://api.apify.com/v2/acts/accountable_eel~linkedin-profile-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>\" \\\n     -X POST \\\n     -H \"Content-Type: application/json\" \\\n     -d '{\"profileUrls\":[\"https://www.linkedin.com/in/williamhgates\",\"satyanadella\"]}'\n   ```\n3. **On a schedule.** Save this actor as an Apify **Task** with the input you want, then add a **Schedule** (hourly, daily, weekly) so it runs on its own — no server of your own required.\n<!-- GENERATED:HOWTO:END -->\n\n1. Open the Input tab and paste your profile links into \"LinkedIn profiles to look up\", one per\n   line. A full link, a bare `linkedin.com/in/...`, or just the identifier after `/in/` all work.\n2. Leave \"Try it first\" on for the first run. It stops after 5 profiles so you can see the shape of\n   the output before spending on the whole list.\n3. Leave the proxy on residential. It is the default, and it is the single biggest factor in how\n   many of your profiles come back rather than blocked.\n4. Keep \"Max concurrency\" low, 2 to 3, on a long list. Speed here buys blocks, not throughput.\n5. Optionally narrow \"Which columns do you want?\", or use \"Only keep rows containing\" and \"Skip\n   rows containing\" to filter by keyword, for example to keep only profiles whose row mentions a\n   city or a job title.\n6. Turn \"Try it first\" off and press Start for the full list.\n\n**A bigger list in one run.** You don't have to run profiles one at a time — paste a whole\nshortlist into `profileUrls` and it processes in one job:\n\n```json\n{\n  \"profileUrls\": [\n    \"https://www.linkedin.com/in/williamhgates\",\n    \"https://www.linkedin.com/in/satyanadella\",\n    \"https://www.linkedin.com/in/reidhoffman\",\n    \"https://www.linkedin.com/in/melindagates\",\n    \"https://www.linkedin.com/in/jeffweiner08\",\n    \"https://www.linkedin.com/in/ariannahuffington\",\n    \"https://www.linkedin.com/in/simonsinek\",\n    \"https://www.linkedin.com/in/brenebrown\",\n    \"https://www.linkedin.com/in/adammgrant\",\n    \"https://www.linkedin.com/in/salliekrawcheck\",\n    \"https://www.linkedin.com/in/rbranson\",\n    \"https://www.linkedin.com/in/garyvaynerchuk\",\n    \"https://www.linkedin.com/in/barackobama\",\n    \"https://www.linkedin.com/in/jenhsunhuang\",\n    \"https://www.linkedin.com/in/bradsmi\",\n    \"https://www.linkedin.com/in/charleslamanna\",\n    \"https://www.linkedin.com/in/michaelabbott\",\n    \"https://www.linkedin.com/in/patmoorhead\",\n    \"https://www.linkedin.com/in/randi-weingarten-05896224\",\n    \"https://www.linkedin.com/in/tony-bates-679227a5\",\n    \"https://www.linkedin.com/in/charlesphillips1\",\n    \"https://www.linkedin.com/in/toufisaliba\",\n    \"https://www.linkedin.com/in/claire-hughes-johnson-7058\",\n    \"https://www.linkedin.com/in/lennyrachitsky\",\n    \"https://www.linkedin.com/in/binoyg\"\n  ]\n}\n```\n\n25 profiles ≈ **$0.20** if every one is found (25 × $0.008 per found row, plus the $0.00005 actor\nstart fee) — less for any that come back blocked or missing, since a miss is never charged.\n\n## Input\n\n<!-- GENERATED:INPUT:START -->\n```json\n{\n  \"profileUrls\": [\n    \"https://www.linkedin.com/in/williamhgates\",\n    \"satyanadella\"\n  ]\n}\n```\n\nOne profile per line. Paste the full profile link, or just the part after /in/. Accepted formats: https://www.linkedin.com/in/williamhgates, linkedin.com/in/williamhgates, williamhgates.\n<!-- GENERATED:INPUT:END -->\n\n## Output\n\nOne row per profile submitted:\n\n<!-- GENERATED:SAMPLE_OUTPUT:START -->\n| query | found | status | profileUrl | publicId | fullName | headline | location | currentTitle | currentCompany | currentCompanyUrl | experience | education | followers | connections | profileImageUrl | source | masked | changed | changeType | previousCompany | previousTitle | monitorFirstSeenAt | experience1Company | experience1Title | experience1Start | experience2Company | experience2Title | experience2Start | experience3Company | experience3Title | experience3Start | education1School | education1Degree | companyDomain | emailGuess | emailCandidates | emailDomainStatus | mxProvider | mailboxVerified | scrapedAt |\n| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |\n| reidhoffman | true | OK | https://www.linkedin.com/in/reidhoffman | reidhoffman | Reid Hoffman | Co-Founder, LinkedIn, Manas AI & Inflection AI. Founding Team, PayPal. Author of Superagency. Podcaster of Possible and Masters of Scale. | United States | Co-Founder, Executive Board Chair | Manas AI | https://www.linkedin.com/company/manas-ai | [{\"title\":\"Co-Founder, Executive Board Chair\",\"company\":\"Manas AI\",\"companyUrl\":\"https://www.linkedin.com/company/manas-ai\",\"location\":null,\"startDate\":\"2025-01\",\"endDate\":null},{\"title\":\"Co-Founde... | [{\"school\":\"Università degli Studi di Perugia\",\"degree\":null,\"startDate\":\"2024-05\",\"endDate\":\"2024-05\"},{\"school\":\"University of Oulu\",\"degree\":null,\"startDate\":\"2020\",\"endDate\":\"2020\"},{\"school\":\"... | 2796136 | 500+ | https://media.licdn.com/dms/image/v2/D5603AQHW7wKzPb3DAg/profile-displayphoto-shrink_200_200/profile-displayphoto-shrink_200_200/0/1718216204174 | jsonld | false |  |  |  |  |  | <role 1: company> | <role 1: title> | <role 1: start> | <role 2: company> | <role 2: title> | <role 2: start> | <role 3: company> | <role 3: title> | <role 3: start> | <school 1> | <school 1: degree> |  |  |  |  |  |  | 2026-10-09T06:01:17.079Z |\n<!-- GENERATED:SAMPLE_OUTPUT:END -->\n\nA profile that could not be read comes back as a row with `\"found\": false`, a `status`, and a\n`message` explaining which of the four reasons it was, and is never charged.\n\n## Tips\n\n- Run a handful of profiles you know by heart first. It is the fastest way to confirm the fields\n  land where you expect before you point a long list at it.\n- Filter on `masked: true` rather than trusting an empty `currentTitle`. A masked row is LinkedIn\n  trimming the page for that request, not the member leaving the field blank. If the headline\n  matters to you, treat a masked row as \"ask again later\", not as \"this person has no headline\".\n- `publicId` is the join key, not `fullName`. People change their display name and their headline;\n  the identifier after `/in/` is what stays the same.\n- A wave of `BLOCKED` rows means the addresses you are running from are being asked to sign in.\n  Lower \"Max concurrency\", leave residential proxy on, and re-run the blocked rows later. They cost\n  you nothing the first time.\n- Store `followers` as a number and `connections` as text. LinkedIn caps the connection display at\n  `500+`, so forcing it into a numeric column silently loses the distinction between 500 and 30,000.\n\n## vs. alternatives\n\n| | What it costs | What you get | Trade-off |\n|---|---|---|---|\n| **This actor** (`linkedin-profile-lookup`) | $0.008 per profile found at the FREE tier, less on paid tiers, $0.00005 actor start, nothing for a miss | One flat row per public profile: name, location, current company, current roles with dates, schools with dates, follower and connection counts, photo, and the headline and job title on the rows LinkedIn serves in full | Public profile pages only. Current roles, not full career history; no degree, field of study or skills, because the logged-out page does not publish them; and the headline and job title are missing whenever LinkedIn reduces the page, flagged by `masked`. Email guesses are pattern-based, not mailbox-verified. It looks up profiles you name; it does not search for people. |\n| **HarvestAPI's LinkedIn profile actors** | `linkedin-profile-search` is listed at $100 per 1,000 profiles | A whole family: profile lookup, people search, company and post coverage, and the category's most-used profile scraper at a 4.77 rating | If you need to *find* people by criteria rather than look up links you already hold, that is a search product and this is not one. |\n| **Clay** | $0.08 to $0.40 per enriched row in credits, on top of a seat | A whole enrichment workspace: waterfalls across dozens of providers, plus the table and the sequencing around it | If you want one place that does everything and you are not counting rows, that is Clay. This is one column, priced per column, callable *from* Clay via its HTTP step. |\n| Doing it yourself | Your time, plus residential proxy spend, plus keeping up with a sign-in wall that moves | The same fields | What this absorbs: the structured-data parsing, the reduced-page detection so asterisks never reach your table, the block-versus-missing-versus-bad-input distinction, and retrying a blocked address instead of failing the row. |\n\nPrices for third-party tools are their published list prices as of September 2026 and are not\ntracked here. Check the vendor before relying on the comparison.\n\n## FAQ\n\n**Do I need a LinkedIn account, or to paste in a cookie?**\nNo. That is the point of this actor. It reads the public profile page exactly as a signed-out\nbrowser gets it. You are never asked for credentials, a session cookie or a `li_at` token, and no\naccount of yours is put at risk by a run.\n\n**Why did a row come back with `found: false`?**\nOne of four reasons, and `status` says which. `BLOCKED`: LinkedIn asked that request to sign in\ninstead of serving the page. `NOT_FOUND`: no public profile exists at that address any more, or the\nmember has turned public visibility off. `BAD_FORMAT`: the input was not a member profile, most\noften a `/company/` page pasted by mistake. `REQUEST_FAILED`: the request did not complete after\nretries. None of the four is charged.\n\n**What is `masked: true`?**\nLinkedIn answers some requests with a reduced version of the profile in which job titles and past\nemployers are replaced by rows of asterisks. Rather than pass that through, the actor leaves those\nfields empty and sets `masked: true`. Name, location, current employer, follower and connection\ncounts, schools and photo still come through, so the row is worth having. How often it happens is\nLinkedIn's choice and it changes: on 2026-09-08 most fetches were reduced, and from 2026-09-09 to\n2026-09-16 none were (0 of 228 rows). It looks like a per-request decision rather than a property of\nthe profile or of where you run from, so re-running a masked row later is worth a try, and it costs\nyou a second charge for that profile.\n\n**Are the email addresses verified?**\nNo. `emailGuess` is guessed from the company's email pattern, not verified at the mailbox. The\nactor checks that the company domain can receive mail at all and ranks the usual address patterns\n(`first.last`, `first`, `firstlast`) by how common they are, but it never contacts a mail server,\nbecause cloud platforms block the port that would need. `mailboxVerified` is always `false` for\nthat reason. Run the guesses through a verification tool before a large send.\n\n**Why is `education[].degree` null?**\nBecause the logged-out profile page does not publish it. Degree and field of study are shown to\nsigned-in members only. The field is kept in the row so the output shape never changes between\nruns, but it is left null rather than filled from a guess.\n\n**Why is there no skills column?**\nBecause the logged-out page has no Skills section. None of the fifteen public profile fetches this\nactor was built against carried one, in either version of the page, so a skills column would be\nempty on every row. It is better to say that here than to ship the column.\n\n**Does it return full career history?**\nNo. The public page shows the roles a member presents as current, and that is what `experience`\ncontains. Everything earlier is behind the sign-in wall, and this actor does not go there.\n\n**Am I charged for a miss?**\nNo. Billing fires only when a real profile was read. Blocked rows, missing profiles and malformed\ninput all produce a row explaining themselves, unless you turn on \"Hide rows with no result\", and\nnone of them cost anything.\n\n**Do I need to configure a proxy?**\nIt is already configured. Residential proxy is the shipped default because LinkedIn's sign-in wall\nkeys on the reputation of the address asking. You can change the group in the Advanced section, but\nlowering it is the most common reason a list comes back mostly blocked.\n\n**Is this a live fetch or a stored database?**\nLive. Every run fetches the current page. Nothing is stored between runs, so a member who changed\njobs yesterday shows the new one today.\n\n**Can an AI agent call this directly?**\nYes. It is on the Apify MCP server, so an agent in Claude, Cursor or any other MCP client can find\nand run it by name, and the REST endpoint above works from any script or workflow tool.\n\n## Personal data, and your responsibility\n\nThis actor reads public LinkedIn profile pages, which are personal data about identifiable people.\nIt reads nothing behind the sign-in wall and it holds nothing between runs: each row is fetched\nlive and written to your own dataset, which only you control.\n\nWhether you may collect, store and use that data is a question about you and your purpose, not\nabout this tool. If you are in the EU or the UK, or your subjects are, the GDPR applies to what you\ndo next: you need a lawful basis, and duties around transparency, retention, and honouring a\nrequest to be erased. LinkedIn's own User Agreement also restricts automated collection, and it\nbinds anyone using it under an account. Nothing here is legal advice, and running this actor is not\na determination that your use is lawful. That call, and the compliance work behind it, is yours.\nA guessed work email is personal data too, and sending unsolicited email to one brings its own rules\n(for example the ePrivacy rules in the EU and the UK, and CAN-SPAM in the US).\n\n## Related actors\n\n\n- [LinkedIn Profile Posts Scraper: by URL, Without Login](https://apify.com/accountable_eel/linkedin-profile-posts-lookup): get a profile's latest posts by URL, without a login.\n\n- [LinkedIn Jobs Search Lookup](https://apify.com/accountable_eel/linkedin-jobs-search-lookup) run\n  a public LinkedIn job search by keyword and location and get one row per posting.\n- [Company Domain Enrichment](https://apify.com/accountable_eel/company-domain-enrichment) turn the\n  employer you just found into firmographics: registration, DNS, tech stack, hiring signal.\n- [Email Pattern Finder](https://apify.com/accountable_eel/email-pattern-finder) take the company\n  domain this actor found and work out that company's email pattern in more depth than the built-in\n  \"Guess each person's work email\" option here.\n- [Email Deliverability Check](https://apify.com/accountable_eel/email-deliverability-check) verify\n  the guessed address actually has a working mail route before you send to it.\n\nIf this saved you a scrape, a rating on the Store page helps other buyers find it.\n",
    "actorDefinition": {
      "actorSpecification": 1,
      "name": "linkedin-profile-lookup",
      "title": "LinkedIn Profile Scraper Without Login: Bulk URLs + Work Email",
      "description": "LinkedIn profile scraper without login and no cookies: paste a list of public profile URLs and get one row per person - name, job title, company, location, follower count. Find a work email guess from each LinkedIn URL. Schedule it for job change alerts: monitor mode flags who moved company.",
      "version": "1.0",
      "buildTag": "latest",
      "input": {
        "title": "LinkedIn Profile Scraper Without Login: Bulk URLs + Work Email input",
        "type": "object",
        "schemaVersion": 1,
        "properties": {
          "profileUrls": {
            "title": "LinkedIn profiles to look up",
            "type": "array",
            "description": "One profile per line. Paste the full profile link, or just the part after /in/. Accepted formats: https://www.linkedin.com/in/williamhgates, linkedin.com/in/williamhgates, williamhgates. You're only charged for the ones we actually find — a miss costs nothing.",
            "editor": "stringList",
            "prefill": [
              "https://www.linkedin.com/in/williamhgates",
              "satyanadella"
            ]
          },
          "testRun": {
            "title": "Try it first — only process the first 5",
            "type": "boolean",
            "description": "Turn this on to test your input on a small sample before running the full list. Turn it off to process everything.",
            "editor": "checkbox",
            "default": false
          },
          "onlyFound": {
            "title": "Hide rows with no result",
            "type": "boolean",
            "description": "Only keep rows where something was actually found. Misses are always free, whether or not you show them here.",
            "editor": "checkbox",
            "default": false,
            "sectionCaption": "🔎 Filter results"
          },
          "includeKeywords": {
            "title": "Only keep rows containing",
            "type": "array",
            "description": "Optional. Only keep results that mention at least one of these words (e.g. a job title, a city, a product name). Leave empty to keep everything.",
            "editor": "stringList",
            "prefill": []
          },
          "excludeKeywords": {
            "title": "Skip rows containing",
            "type": "array",
            "description": "Optional. Drop any result that mentions one of these words. Leave empty to skip nothing.",
            "editor": "stringList",
            "prefill": []
          },
          "maxResults": {
            "title": "Stop after this many results",
            "type": "integer",
            "description": "Optional. Stop the run once this many results have been found — useful for a quick, cheap sample. Leave blank for no limit.",
            "editor": "number",
            "minimum": 1,
            "nullable": true
          },
          "deltaMode": {
            "title": "Flag job changes since the last run",
            "type": "boolean",
            "description": "Turns a scheduled list into a job-change monitor. Each profile's current company, job title and location are remembered between runs; every row still comes back, with 'Job change since last run', 'What changed', 'Previous company' and 'Previous job title' filled in, so you filter on the change. The first run records everyone as first-seen. A field LinkedIn hides on a given run is never reported as a change. Billing is unchanged: one charge per profile found.",
            "editor": "checkbox",
            "default": false,
            "sectionCaption": "🔔 Monitoring"
          },
          "deltaName": {
            "title": "Watchlist name (optional)",
            "type": "string",
            "description": "Leave empty to use one shared memory for this account. Type a name (for example 'sales-accounts' or 'candidates-q4') to keep two schedules of this actor from overwriting each other's memory of the same person.",
            "editor": "textfield",
            "default": ""
          },
          "flatten": {
            "title": "Spreadsheet columns (one row = one line)",
            "type": "boolean",
            "description": "Adds the first three roles and the first school as plain text columns (Role 1: company, Role 1: title, Role 1: start, and so on), so a Google Sheets, Clay, Excel or n8n export needs no JSON parsing. The nested 'Current roles listed' and 'Schools listed' columns stay as they are. Pick the 'Spreadsheet' view in the Output tab for just those columns.",
            "editor": "checkbox",
            "default": false,
            "sectionCaption": "📊 Spreadsheet export"
          },
          "findEmail": {
            "title": "Guess each person's work email",
            "type": "boolean",
            "description": "Reads the current employer's public LinkedIn company page for its website domain (once per company per run), checks that the domain can receive mail, and fills 'Email guess' with the most likely address plus 2 alternatives. Guessed from the company's email pattern, not verified at the mailbox. No extra charge: still one charge per profile found. Adds one company-page request per distinct employer.",
            "editor": "checkbox",
            "default": false,
            "sectionCaption": "📧 Email"
          },
          "columns": {
            "title": "Which columns do you want?",
            "type": "array",
            "description": "Choose which pieces of information to include in each result row. All are included by default.",
            "editor": "select",
            "items": {
              "type": "string",
              "enum": [
                "profileUrl",
                "publicId",
                "fullName",
                "headline",
                "location",
                "currentTitle",
                "currentCompany",
                "currentCompanyUrl",
                "experience",
                "education",
                "followers",
                "connections",
                "profileImageUrl",
                "source",
                "masked",
                "changed",
                "changeType",
                "previousCompany",
                "previousTitle",
                "monitorFirstSeenAt",
                "experience1Company",
                "experience1Title",
                "experience1Start",
                "experience2Company",
                "experience2Title",
                "experience2Start",
                "experience3Company",
                "experience3Title",
                "experience3Start",
                "education1School",
                "education1Degree",
                "companyDomain",
                "emailGuess",
                "emailCandidates",
                "emailDomainStatus",
                "mxProvider",
                "mailboxVerified"
              ],
              "enumTitles": [
                "Profile link",
                "Public profile ID",
                "Name",
                "Headline",
                "Location",
                "Current job title",
                "Current company",
                "Current company page",
                "Current roles listed",
                "Schools listed",
                "Followers",
                "Connections",
                "Profile photo",
                "Read from",
                "LinkedIn hid part of this page",
                "Job change since last run",
                "What changed",
                "Previous company",
                "Previous job title",
                "Monitored since",
                "Role 1: company",
                "Role 1: title",
                "Role 1: start",
                "Role 2: company",
                "Role 2: title",
                "Role 2: start",
                "Role 3: company",
                "Role 3: title",
                "Role 3: start",
                "School 1",
                "School 1: degree",
                "Company domain",
                "Email guess",
                "Other likely addresses",
                "Email domain status",
                "Mail provider",
                "Mailbox verified"
              ]
            },
            "default": [
              "profileUrl",
              "publicId",
              "fullName",
              "headline",
              "location",
              "currentTitle",
              "currentCompany",
              "currentCompanyUrl",
              "experience",
              "education",
              "followers",
              "connections",
              "profileImageUrl",
              "source",
              "masked",
              "changed",
              "changeType",
              "previousCompany",
              "previousTitle",
              "monitorFirstSeenAt",
              "experience1Company",
              "experience1Title",
              "experience1Start",
              "experience2Company",
              "experience2Title",
              "experience2Start",
              "experience3Company",
              "experience3Title",
              "experience3Start",
              "education1School",
              "education1Degree",
              "companyDomain",
              "emailGuess",
              "emailCandidates",
              "emailDomainStatus",
              "mxProvider",
              "mailboxVerified"
            ],
            "sectionCaption": "📄 Columns & format"
          },
          "maxConcurrency": {
            "title": "Max concurrency",
            "type": "integer",
            "description": "Parallel requests. Keep conservative — this target has no browser fallback, so getting blocked costs more than slow-and-steady.",
            "default": 2,
            "minimum": 1,
            "maximum": 8,
            "sectionCaption": "⚙️ Advanced — you can ignore this"
          },
          "proxyConfiguration": {
            "title": "Proxy configuration",
            "type": "object",
            "description": "Apify Proxy config. Residential recommended for anti-bot-sensitive targets.",
            "editor": "proxy",
            "default": {
              "useApifyProxy": true,
              "apifyProxyGroups": [
                "RESIDENTIAL"
              ]
            }
          }
        },
        "required": [
          "profileUrls"
        ]
      },
      "output": {
        "$schema": "https://apify.com/schemas/v1/output.ide.json",
        "actorOutputSchemaVersion": 1,
        "title": "Output schema",
        "properties": {
          "results": {
            "type": "string",
            "title": "Results",
            "template": "{{links.apiDefaultDatasetUrl}}/items"
          }
        }
      },
      "storages": {
        "dataset": {
          "actorSpecification": 1,
          "fields": {
            "type": "object",
            "properties": {
              "query": {
                "type": "string",
                "description": "The input item that was looked up."
              },
              "found": {
                "type": "boolean",
                "description": "Whether a result was found. false rows are never charged."
              },
              "status": {
                "type": "string",
                "description": "'OK' when found, or a reason code (NOT_FOUND / BAD_FORMAT / BLOCKED / REQUEST_FAILED) when not."
              },
              "message": {
                "type": "string",
                "description": "Plain-English explanation of the status, present on rows where nothing was found."
              },
              "scrapedAt": {
                "type": "string",
                "description": "ISO timestamp of extraction."
              },
              "profileUrl": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Profile link (URL)."
              },
              "publicId": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Public profile ID"
              },
              "fullName": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Name"
              },
              "headline": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Headline"
              },
              "location": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Location"
              },
              "currentTitle": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Current job title"
              },
              "currentCompany": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Current company"
              },
              "currentCompanyUrl": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Current company page (URL)."
              },
              "experience": {
                "type": [
                  "array",
                  "null"
                ],
                "description": "Current roles listed"
              },
              "education": {
                "type": [
                  "array",
                  "null"
                ],
                "description": "Schools listed"
              },
              "followers": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "Followers"
              },
              "connections": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Connections"
              },
              "profileImageUrl": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Profile photo (URL)."
              },
              "source": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Read from"
              },
              "masked": {
                "type": [
                  "boolean",
                  "null"
                ],
                "description": "LinkedIn hid part of this page"
              },
              "changed": {
                "type": [
                  "boolean",
                  "null"
                ],
                "description": "Monitoring only. true when the current company, job title or location differs from what the previous run of this watchlist read for the profile. false on a first sighting or no change; null when monitoring is off."
              },
              "changeType": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Monitoring only. company, title, location, none, or first-seen. When several changed, the first in that order. A field LinkedIn hid on this run's page is never counted as a change. null when monitoring is off."
              },
              "previousCompany": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Monitoring only. The current company the previous run remembered for this profile. null on a first sighting or when monitoring is off."
              },
              "previousTitle": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Monitoring only. The current job title the previous run remembered for this profile. null on a first sighting, when no run has read a title yet, or when monitoring is off."
              },
              "monitorFirstSeenAt": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Monitoring only. When this watchlist first looked the profile up (ISO timestamp). null when monitoring is off."
              },
              "experience1Company": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Spreadsheet export only. Company of the first role listed on the public page, as plain text. Only present when flatten is on."
              },
              "experience1Title": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Spreadsheet export only. Job title of that role, as plain text; null when LinkedIn hid it. Only present when flatten is on."
              },
              "experience1Start": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Spreadsheet export only. Start year or date of that role, as plain text. Only present when flatten is on."
              },
              "experience2Company": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Spreadsheet export only. Company of the second role listed on the public page, as plain text. Only present when flatten is on."
              },
              "experience2Title": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Spreadsheet export only. Job title of that role, as plain text; null when LinkedIn hid it. Only present when flatten is on."
              },
              "experience2Start": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Spreadsheet export only. Start year or date of that role, as plain text. Only present when flatten is on."
              },
              "experience3Company": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Spreadsheet export only. Company of the third role listed on the public page, as plain text. Only present when flatten is on."
              },
              "experience3Title": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Spreadsheet export only. Job title of that role, as plain text; null when LinkedIn hid it. Only present when flatten is on."
              },
              "experience3Start": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Spreadsheet export only. Start year or date of that role, as plain text. Only present when flatten is on."
              },
              "education1School": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Spreadsheet export only. First school listed on the public page, as plain text. Only present when flatten is on."
              },
              "education1Degree": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Spreadsheet export only. Degree for that school. LinkedIn's logged-out page does not publish degrees, so this is null today; the column is kept so a sheet's layout stays fixed. Only present when flatten is on."
              },
              "companyDomain": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Email guess only. The current employer's website domain, read from its public LinkedIn company page. null when findEmail is off, the company page lists no website, or the website is a social or link-hub page."
              },
              "emailGuess": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Email guess only. The most likely work address, guessed from the company's email pattern, not verified at the mailbox. null when findEmail is off, no domain was found, or the domain cannot receive mail."
              },
              "emailCandidates": {
                "type": [
                  "array",
                  "null"
                ],
                "description": "Email guess only. The top 3 addresses by how common each pattern is, each as {pattern, email, confidence}. confidence is the pattern's general popularity (0-1), not a mailbox check."
              },
              "emailDomainStatus": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Email guess only. invalid (the domain has no mail route, so no address there works), catch-all (a disposable-mail domain), or unknown (a real mail route; the mailbox itself cannot be checked). Never valid."
              },
              "mxProvider": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Email guess only. google, microsoft or other, from the domain's MX records. null when there are none."
              },
              "mailboxVerified": {
                "type": [
                  "boolean",
                  "null"
                ],
                "description": "Email guess only. Always false when findEmail is on: no mailbox is ever contacted. null when findEmail is off."
              }
            },
            "required": [
              "query",
              "found",
              "status",
              "scrapedAt"
            ]
          },
          "views": {
            "people": {
              "title": "People",
              "transformation": {
                "fields": [
                  "query",
                  "found",
                  "fullName",
                  "headline",
                  "currentTitle",
                  "currentCompany",
                  "location",
                  "profileUrl",
                  "masked"
                ]
              },
              "display": {
                "component": "table",
                "properties": {
                  "query": {
                    "label": "Profile submitted",
                    "format": "text"
                  },
                  "found": {
                    "label": "Found?",
                    "format": "boolean"
                  },
                  "fullName": {
                    "label": "Name",
                    "format": "text"
                  },
                  "headline": {
                    "label": "Headline",
                    "format": "text"
                  },
                  "currentTitle": {
                    "label": "Current job title",
                    "format": "text"
                  },
                  "currentCompany": {
                    "label": "Current company",
                    "format": "text"
                  },
                  "location": {
                    "label": "Location",
                    "format": "text"
                  },
                  "profileUrl": {
                    "label": "Profile link",
                    "format": "link"
                  },
                  "masked": {
                    "label": "LinkedIn hid part of this page",
                    "format": "boolean"
                  }
                }
              }
            },
            "spreadsheet": {
              "title": "Spreadsheet",
              "transformation": {
                "fields": [
                  "query",
                  "found",
                  "fullName",
                  "profileUrl",
                  "location",
                  "currentCompany",
                  "currentTitle",
                  "experience1Company",
                  "experience1Title",
                  "experience1Start",
                  "experience2Company",
                  "experience2Title",
                  "experience2Start",
                  "experience3Company",
                  "experience3Title",
                  "experience3Start",
                  "education1School",
                  "education1Degree",
                  "changed",
                  "changeType",
                  "previousCompany",
                  "previousTitle"
                ]
              },
              "display": {
                "component": "table",
                "properties": {
                  "query": {
                    "label": "Profile submitted",
                    "format": "text"
                  },
                  "found": {
                    "label": "Found?",
                    "format": "boolean"
                  },
                  "fullName": {
                    "label": "Name",
                    "format": "text"
                  },
                  "profileUrl": {
                    "label": "Profile link",
                    "format": "link"
                  },
                  "location": {
                    "label": "Location",
                    "format": "text"
                  },
                  "currentCompany": {
                    "label": "Current company",
                    "format": "text"
                  },
                  "currentTitle": {
                    "label": "Current job title",
                    "format": "text"
                  },
                  "experience1Company": {
                    "label": "Role 1: company",
                    "format": "text"
                  },
                  "experience1Title": {
                    "label": "Role 1: title",
                    "format": "text"
                  },
                  "experience1Start": {
                    "label": "Role 1: start",
                    "format": "text"
                  },
                  "experience2Company": {
                    "label": "Role 2: company",
                    "format": "text"
                  },
                  "experience2Title": {
                    "label": "Role 2: title",
                    "format": "text"
                  },
                  "experience2Start": {
                    "label": "Role 2: start",
                    "format": "text"
                  },
                  "experience3Company": {
                    "label": "Role 3: company",
                    "format": "text"
                  },
                  "experience3Title": {
                    "label": "Role 3: title",
                    "format": "text"
                  },
                  "experience3Start": {
                    "label": "Role 3: start",
                    "format": "text"
                  },
                  "education1School": {
                    "label": "School 1",
                    "format": "text"
                  },
                  "education1Degree": {
                    "label": "School 1: degree",
                    "format": "text"
                  },
                  "changed": {
                    "label": "Job change since last run",
                    "format": "boolean"
                  },
                  "changeType": {
                    "label": "What changed",
                    "format": "text"
                  },
                  "previousCompany": {
                    "label": "Previous company",
                    "format": "text"
                  },
                  "previousTitle": {
                    "label": "Previous job title",
                    "format": "text"
                  }
                }
              }
            },
            "email": {
              "title": "Email guess",
              "transformation": {
                "fields": [
                  "query",
                  "found",
                  "fullName",
                  "currentTitle",
                  "currentCompany",
                  "companyDomain",
                  "emailGuess",
                  "emailCandidates",
                  "emailDomainStatus",
                  "mxProvider",
                  "mailboxVerified",
                  "profileUrl"
                ]
              },
              "display": {
                "component": "table",
                "properties": {
                  "query": {
                    "label": "Profile submitted",
                    "format": "text"
                  },
                  "found": {
                    "label": "Found?",
                    "format": "boolean"
                  },
                  "fullName": {
                    "label": "Name",
                    "format": "text"
                  },
                  "currentTitle": {
                    "label": "Current job title",
                    "format": "text"
                  },
                  "currentCompany": {
                    "label": "Current company",
                    "format": "text"
                  },
                  "companyDomain": {
                    "label": "Company domain",
                    "format": "text"
                  },
                  "emailGuess": {
                    "label": "Email guess",
                    "format": "text"
                  },
                  "emailCandidates": {
                    "label": "Other likely addresses",
                    "format": "array"
                  },
                  "emailDomainStatus": {
                    "label": "Email domain status",
                    "format": "text"
                  },
                  "mxProvider": {
                    "label": "Mail provider",
                    "format": "text"
                  },
                  "mailboxVerified": {
                    "label": "Mailbox verified",
                    "format": "boolean"
                  },
                  "profileUrl": {
                    "label": "Profile link",
                    "format": "link"
                  }
                }
              }
            },
            "employment": {
              "title": "Employment",
              "transformation": {
                "fields": [
                  "query",
                  "found",
                  "fullName",
                  "currentTitle",
                  "currentCompany",
                  "currentCompanyUrl",
                  "experience",
                  "education",
                  "profileUrl"
                ]
              },
              "display": {
                "component": "table",
                "properties": {
                  "query": {
                    "label": "Profile submitted",
                    "format": "text"
                  },
                  "found": {
                    "label": "Found?",
                    "format": "boolean"
                  },
                  "fullName": {
                    "label": "Name",
                    "format": "text"
                  },
                  "currentTitle": {
                    "label": "Current job title",
                    "format": "text"
                  },
                  "currentCompany": {
                    "label": "Current company",
                    "format": "text"
                  },
                  "currentCompanyUrl": {
                    "label": "Current company page",
                    "format": "link"
                  },
                  "experience": {
                    "label": "Current roles listed",
                    "format": "array"
                  },
                  "education": {
                    "label": "Schools listed",
                    "format": "array"
                  },
                  "profileUrl": {
                    "label": "Profile link",
                    "format": "link"
                  }
                }
              }
            },
            "audience": {
              "title": "Audience",
              "transformation": {
                "fields": [
                  "query",
                  "found",
                  "fullName",
                  "headline",
                  "followers",
                  "connections",
                  "profileImageUrl",
                  "profileUrl"
                ]
              },
              "display": {
                "component": "table",
                "properties": {
                  "query": {
                    "label": "Profile submitted",
                    "format": "text"
                  },
                  "found": {
                    "label": "Found?",
                    "format": "boolean"
                  },
                  "fullName": {
                    "label": "Name",
                    "format": "text"
                  },
                  "headline": {
                    "label": "Headline",
                    "format": "text"
                  },
                  "followers": {
                    "label": "Followers",
                    "format": "number"
                  },
                  "connections": {
                    "label": "Connections",
                    "format": "text"
                  },
                  "profileImageUrl": {
                    "label": "Profile photo",
                    "format": "image"
                  },
                  "profileUrl": {
                    "label": "Profile link",
                    "format": "link"
                  }
                }
              }
            },
            "overview": {
              "title": "Overview",
              "transformation": {
                "fields": [
                  "query",
                  "found",
                  "status",
                  "message",
                  "profileUrl",
                  "publicId",
                  "fullName",
                  "headline",
                  "location",
                  "currentTitle",
                  "currentCompany",
                  "currentCompanyUrl",
                  "experience",
                  "education",
                  "followers",
                  "connections",
                  "profileImageUrl",
                  "source",
                  "masked",
                  "changed",
                  "changeType",
                  "previousCompany",
                  "previousTitle",
                  "monitorFirstSeenAt",
                  "experience1Company",
                  "experience1Title",
                  "experience1Start",
                  "experience2Company",
                  "experience2Title",
                  "experience2Start",
                  "experience3Company",
                  "experience3Title",
                  "experience3Start",
                  "education1School",
                  "education1Degree",
                  "companyDomain",
                  "emailGuess",
                  "emailCandidates",
                  "emailDomainStatus",
                  "mxProvider",
                  "mailboxVerified",
                  "scrapedAt"
                ]
              },
              "display": {
                "component": "table",
                "properties": {
                  "query": {
                    "label": "Profile submitted",
                    "format": "text"
                  },
                  "found": {
                    "label": "Found?",
                    "format": "boolean"
                  },
                  "status": {
                    "label": "Status",
                    "format": "text"
                  },
                  "message": {
                    "label": "Details",
                    "format": "text"
                  },
                  "scrapedAt": {
                    "label": "Checked at",
                    "format": "date"
                  },
                  "profileUrl": {
                    "label": "Profile link",
                    "format": "link"
                  },
                  "publicId": {
                    "label": "Public profile ID",
                    "format": "text"
                  },
                  "fullName": {
                    "label": "Name",
                    "format": "text"
                  },
                  "headline": {
                    "label": "Headline",
                    "format": "text"
                  },
                  "location": {
                    "label": "Location",
                    "format": "text"
                  },
                  "currentTitle": {
                    "label": "Current job title",
                    "format": "text"
                  },
                  "currentCompany": {
                    "label": "Current company",
                    "format": "text"
                  },
                  "currentCompanyUrl": {
                    "label": "Current company page",
                    "format": "link"
                  },
                  "experience": {
                    "label": "Current roles listed",
                    "format": "array"
                  },
                  "education": {
                    "label": "Schools listed",
                    "format": "array"
                  },
                  "followers": {
                    "label": "Followers",
                    "format": "number"
                  },
                  "connections": {
                    "label": "Connections",
                    "format": "text"
                  },
                  "profileImageUrl": {
                    "label": "Profile photo",
                    "format": "image"
                  },
                  "source": {
                    "label": "Read from",
                    "format": "text"
                  },
                  "masked": {
                    "label": "LinkedIn hid part of this page",
                    "format": "boolean"
                  },
                  "changed": {
                    "label": "Job change since last run",
                    "format": "boolean"
                  },
                  "changeType": {
                    "label": "What changed",
                    "format": "text"
                  },
                  "previousCompany": {
                    "label": "Previous company",
                    "format": "text"
                  },
                  "previousTitle": {
                    "label": "Previous job title",
                    "format": "text"
                  },
                  "monitorFirstSeenAt": {
                    "label": "Monitored since",
                    "format": "date"
                  },
                  "experience1Company": {
                    "label": "Role 1: company",
                    "format": "text"
                  },
                  "experience1Title": {
                    "label": "Role 1: title",
                    "format": "text"
                  },
                  "experience1Start": {
                    "label": "Role 1: start",
                    "format": "text"
                  },
                  "experience2Company": {
                    "label": "Role 2: company",
                    "format": "text"
                  },
                  "experience2Title": {
                    "label": "Role 2: title",
                    "format": "text"
                  },
                  "experience2Start": {
                    "label": "Role 2: start",
                    "format": "text"
                  },
                  "experience3Company": {
                    "label": "Role 3: company",
                    "format": "text"
                  },
                  "experience3Title": {
                    "label": "Role 3: title",
                    "format": "text"
                  },
                  "experience3Start": {
                    "label": "Role 3: start",
                    "format": "text"
                  },
                  "education1School": {
                    "label": "School 1",
                    "format": "text"
                  },
                  "education1Degree": {
                    "label": "School 1: degree",
                    "format": "text"
                  },
                  "companyDomain": {
                    "label": "Company domain",
                    "format": "text"
                  },
                  "emailGuess": {
                    "label": "Email guess",
                    "format": "text"
                  },
                  "emailCandidates": {
                    "label": "Other likely addresses",
                    "format": "array"
                  },
                  "emailDomainStatus": {
                    "label": "Email domain status",
                    "format": "text"
                  },
                  "mxProvider": {
                    "label": "Mail provider",
                    "format": "text"
                  },
                  "mailboxVerified": {
                    "label": "Mailbox verified",
                    "format": "boolean"
                  }
                }
              }
            }
          }
        }
      },
      "readme": "# LinkedIn Profile Lookup: LinkedIn Profile Scraper, No Cookies\n\n**Paste a list of LinkedIn profile URLs and get back a spreadsheet: who each person is, where they work, and what their job title is today.**\n\n## Who uses this\n\n- **Sales and SDR teams** with a column of LinkedIn URLs in the CRM, who need name, title and company filled in before writing the first line of an email.\n- **Recruiters** checking whether a shortlist of 200 candidates still holds the job their notes say they do.\n- **RevOps and data teams** piping rows into Google Sheets, Clay or n8n so a lead record is complete the moment it lands.\n\n## Try it in 30 seconds\n\nThe input box is already filled in with two real public profiles. Click **Start** and you get one row per person, with the columns below. No login, no cookies, no `li_at` session token to paste in.\n\n## Example output\n\nReal rows from a recent run:\n\n| fullName | currentTitle | currentCompany | location | followers |\n|---|---|---|---|---|\n| Bill Gates | Co-chair | Gates Foundation | Seattle, Washington, United States | 40,689,855 |\n| Reid Hoffman | Co-Founder, Executive Board Chair | Manas AI | United States | 2,794,979 |\n| Satya Nadella | Chairman and CEO | Microsoft | Redmond, Washington, United States | 12,195,120 |\n\nEvery row also carries job history with start and end years, schools, connection count, the profile photo, and an optional work email guess.\n\n## What it costs\n\nAbout **$4 per 1,000 profiles found**. A profile that can't be read is free, so a list with dead or renamed URLs costs you nothing, and first runs fit inside Apify's free credit.\n\nGive this actor a list of public LinkedIn profile links and it fetches each one the way a\nsigned-out browser does, then hands you a flat row per person: name, headline, location, current\njob title and company, the current roles the page lists with their start and end years, schools\nwith years, follower and connection counts, and the profile photo. No login. No cookies. No\n`li_at` session token to paste in, no account of yours at risk, and nothing read from behind\nLinkedIn's sign-in wall.\n\nThe data comes from the `schema.org` Person block LinkedIn itself publishes in the page source of\nevery public profile, so a row is the profile's own structured description of itself rather than a\nguess assembled from scraped text. When LinkedIn decides a request should sign in first, the actor\nsays so in the row's `status` and charges you nothing.\n\n## Who it's for\n\nRecruiters and sourcers who already have a shortlist of profile links and need them as a table\ninstead of forty browser tabs. Sales teams who capture a LinkedIn URL on a form or in a CRM field\nand want the name, title, company and location filled in behind it before the first email goes out.\nFounders checking that an inbound lead's title still matches what their CRM said six months ago.\nAnd anyone building an agent or a workflow that has a profile link and needs the person behind it\nin a shape a script can read, without keeping a logged-in LinkedIn session alive somewhere.\n\nIf you do not have the profile links yet, this is the wrong tool: it looks up profiles you already\nname, it does not search LinkedIn for people matching a description.\n\n## Why this one\n\n- **No cookies, no session token, no account.** Every request is the plain public page. You are\n  never asked to hand over a `li_at` cookie, and no LinkedIn account of yours can be restricted\n  because of a run here.\n- **Read from LinkedIn's own structured data.** The row is built from the `schema.org` Person\n  block in the page source, not from CSS selectors chasing a design that changes monthly. Field\n  names stay put, which is what an agent or a Clay column needs.\n- **It tells you when the page was reduced.** LinkedIn can answer with a trimmed version of the\n  profile in which job titles and past employers are replaced by rows of asterisks. Those never\n  reach your table: the fields come back empty and the row is flagged `masked: true`, so you can\n  filter or re-run instead of importing placeholder text into a CRM. See the note under \"What you\n  get\" for how often that happens, measured rather than guessed.\n- **A block is a labelled miss, not a silent hole.** If LinkedIn asks the request to sign in, the\n  row says `status: BLOCKED` with a plain explanation and costs nothing. A profile that no longer\n  exists says `NOT_FOUND`. A company page pasted by mistake says `BAD_FORMAT`.\n- **Never charged for a miss.** Billing fires only on a row where a real profile came back.\n- **Residential proxy by default.** LinkedIn's sign-in wall is driven by the reputation of the IP\n  asking, so the actor ships with a residential proxy already selected, and retries a blocked\n  request on a fresh address rather than handing you the failure.\n\n## What you get\n\nOne row per profile you submit. Every row starts with the same five fields, then adds the profile\nfields you selected in \"Which columns do you want?\" (all of them by default).\n\n| Field | Type | Description |\n|---|---|---|\n| `query` | text | The profile you submitted, echoed back |\n| `found` | boolean | `true` when a public profile was read |\n| `status` | text | `OK` on a hit, or `NOT_FOUND`, `BLOCKED`, `BAD_FORMAT`, `REQUEST_FAILED` on a miss |\n| `message` | text | Plain-English reason for a miss, present only when `found` is `false` |\n| `scrapedAt` | ISO 8601 datetime | When the page was fetched |\n| `profileUrl` | link | Canonical profile link, `https://www.linkedin.com/in/<public id>` |\n| `publicId` | text | The part of the link after `/in/`, LinkedIn's stable public identifier |\n| `fullName` | text | Name as the public page shows it |\n| `headline` | text | The one-line headline under the name |\n| `location` | text | Location as shown, e.g. `Seattle, Washington, United States` |\n| `currentTitle` | text | Job title of the first current role listed |\n| `currentCompany` | text | Employer for that role |\n| `currentCompanyUrl` | link | That employer's LinkedIn company page |\n| `experience` | array | Current roles the public page lists, each `{title, company, companyUrl, location, startDate, endDate}` |\n| `education` | array | Schools the public page lists, each `{school, degree, startDate, endDate}` |\n| `followers` | number | Exact follower count, taken from the page's structured data rather than the rounded \"41M\" the top card prints |\n| `connections` | text | Connection count as LinkedIn displays it, e.g. `500+`, which is why it is text and not a number |\n| `profileImageUrl` | image | Profile photo on LinkedIn's media CDN |\n| `source` | text | `jsonld` when the row came from the page's structured-data block, `html` when it was assembled from the top card instead |\n| `masked` | boolean | `true` when LinkedIn served the reduced page and hid some titles or employers |\n| `changed` | boolean | Job-change monitor only: `true` when the company, title or location differs from the previous run; `null` when monitoring is off |\n| `changeType` | text | Job-change monitor only: `company`, `title`, `location`, `none` or `first-seen`; `null` when monitoring is off |\n| `previousCompany` | text | Job-change monitor only: the company the previous run remembered |\n| `previousTitle` | text | Job-change monitor only: the job title the previous run remembered |\n| `monitorFirstSeenAt` | ISO 8601 datetime | Job-change monitor only: when this watchlist first looked the profile up |\n| `experience1Company` ... `experience3Start` | text | Spreadsheet columns only: company, title and start of the first three listed roles, as plain text |\n| `education1School`, `education1Degree` | text | Spreadsheet columns only: the first listed school, as plain text (degree is null, see below) |\n| `companyDomain` | text | Email guess only: the current employer's website domain, from its public LinkedIn company page |\n| `emailGuess` | text | Email guess only: the most likely work address, guessed from the company's email pattern, not verified at the mailbox |\n| `emailCandidates` | array | Email guess only: the top 3 addresses, each `{pattern, email, confidence}` |\n| `emailDomainStatus` | text | Email guess only: `invalid` (no mail route), `catch-all` (disposable-mail domain) or `unknown` (real mail route, mailbox not checkable). Never `valid` |\n| `mxProvider` | text | Email guess only: `google`, `microsoft` or `other` |\n| `mailboxVerified` | boolean | Email guess only: always `false`, because no mailbox is ever contacted |\n\nThree honest limits on that table, stated here rather than left for you to discover in the data.\n`education[].degree` is null, because the logged-out page does not publish degree or field of study\nat all. `experience` covers the roles the public page presents as current; full career history is\nshown only to signed-in members and is not fetched. And there is no skills column, because no\nSkills section appeared in any of the fifteen public profile fetches this actor was built and\ntested against, so offering one would mean shipping a column that is always empty.\n\nThe other caveat is `masked`. LinkedIn decides per request whether to serve the complete profile\npage or a reduced one, and how often it reduces has changed over time. On 2026-09-08, the day this\nactor was built, the complete page came back on only four of fifteen fetches. From 2026-09-09 on\nit stopped reducing: 0 of 228 rows this actor returned through 2026-09-16 were masked, and a\ndedicated test on 2026-09-16 got the complete page on all 67 residential requests across ten\nprofiles. On a reduced page the name, location, current employer, follower and connection counts,\nschools and photo still come through; the headline and job titles do not, and the row is flagged\n`masked: true` so you can see exactly which rows those are. Today you should expect headline and\n`currentTitle` on nearly every row, but LinkedIn can switch back without notice, so filter on\n`masked` rather than assuming it never happens.\n\n## Run it on a schedule\n\nTurn a one-off list into a standing watchlist: instead of a fresh snapshot every time, each\nscheduled run tells you which profiles changed company, title or location since the last time you\nlooked.\n\nSet **LinkedIn profiles to look up** (`profileUrls`), turn on **Flag job changes since the last\nrun** (`deltaMode: true`), and optionally give it a **Watchlist name** (`deltaName`) so a second\nschedule doesn't share its memory.\n\nSchedule it in Apify (Schedules > Create) or trigger it from n8n/Make; the delta state lives in a\nnamed key-value store, so every scheduled run reads back exactly where the last one left off. See\n\"Track job changes\" below for the full mechanics.\n\n## Pricing\n\n<!-- GENERATED:PRICE:START -->\n$4 per 1,000 profiles, plus a $0.00005 start fee. Misses (`found:false`) are never charged.\n<!-- GENERATED:PRICE:END -->\n\nA run of 1,000 profiles costs about **$4** if every one comes back, less when some are blocked or\ngone, and less again on a paid Apify plan. Compare that with a per-seat enrichment platform billing\n$0.08 to $0.40 per enriched row on top of a licence, or with LinkedIn profile actors listed at\n$100 per 1,000. Rows that come back `found: false`, for any reason, are free.\n\n## Track job changes\n\nKnow the week a candidate, customer champion or prospect moves to a new company.\n\n1. Put the profiles you care about in **LinkedIn profiles to look up**, one per line.\n2. Turn on **Flag job changes since the last run**. Optionally type a **Watchlist name** such as `champions` so a second list never shares its memory.\n3. Save the input as a task and add a **Schedule**, weekly is enough for most teams.\n4. Add a webhook on **Run succeeded** that triggers an n8n or Make workflow, or posts to Slack. The workflow reads the run's dataset, keeps rows where `changed` is `true`, and sends `fullName`, `previousCompany`, `currentCompany` and `profileUrl`.\n\nThe first run marks everyone `first-seen`. After that each row says `company`, `title`, `location` or `none`. Every profile still comes back, so the same dataset can refresh a Google Sheet in full. LinkedIn sometimes hides job titles on the public page; a hidden value is never reported as a change, so you do not get false alarms. Billing does not change: one charge per profile found.\n\n## Spreadsheet export\n\nGet one clean line per person for Google Sheets, Excel, Clay or an n8n table, with no JSON to unpack.\n\nTurn on **Spreadsheet columns (one row = one line)**. Each row then also carries `experience1Company`, `experience1Title` and `experience1Start`, the same for roles 2 and 3, plus `education1School` and `education1Degree`, all as plain text. In the Output tab pick the **Spreadsheet** view, then export as CSV or Excel, or use Apify's Google Sheets integration to append each run to a sheet.\n\nThe nested `experience` and `education` columns stay available for tools that prefer them. Roles are the ones the public page lists as current, in the order LinkedIn shows them, and `education1Degree` is empty because the logged-out page does not publish degrees. If a saved task lists its own columns, the spreadsheet columns are added anyway once you tick the box. Combine it with the job-change monitor and a weekly schedule to keep a live contact sheet.\n\n## Guess work emails\n\nTurn a LinkedIn profile into a likely work email address, with no extra charge per row.\n\nTurn on **Guess each person's work email**. For every profile, the actor reads the current employer's public LinkedIn company page for its website, keeps the domain (`gatesfoundation.org`), checks that the domain can receive mail, and fills `emailGuess` with the most common company pattern (`bill.gates@gatesfoundation.org`) plus two alternatives in `emailCandidates`. Each company page is read once per run, however many of your profiles work there.\n\nEvery address is **guessed from the company's email pattern, not verified at the mailbox**. No mail server is contacted, so `mailboxVerified` is always `false` and `emailDomainStatus` is never `valid`: `unknown` means the domain has a real mail route, `invalid` means nothing can be delivered there (and no guess is given), `catch-all` means a disposable-mail domain. Confirm an address with a verification tool before you send to it at volume. A current employer that is a school, or a company page with no website or only a social link as its website, leaves the email columns empty. In a test on 2026-09-16, 25 of 25 company pages published a website, and all 25 domains had a mail route; all 6 school pages were blocked. Billing does not change: one charge per profile found.\n\n## Use it from Clay, n8n, Make, or an AI agent\n\n<!-- GENERATED:INTEGRATIONS:START -->\nThis actor runs synchronously over plain HTTP — call it directly from a script, a workflow tool, or an AI agent, no Apify Console needed once you have an API token.\n\n```bash\ncurl \"https://api.apify.com/v2/acts/accountable_eel~linkedin-profile-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>\" \\\n  -X POST \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"profileUrls\":[\"https://www.linkedin.com/in/williamhgates\",\"satyanadella\"]}'\n```\n\n**n8n.** Add an HTTP Request node: Method `POST`, URL `https://api.apify.com/v2/acts/accountable_eel~linkedin-profile-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>`, Body Content Type `JSON`, JSON Body `{\"profileUrls\":[\"https://www.linkedin.com/in/williamhgates\",\"satyanadella\"]}` (swap in an expression from an earlier node for a real value).\n\n**Clay.** Add an \"HTTP API\" column: Method `POST`, URL `https://api.apify.com/v2/acts/accountable_eel~linkedin-profile-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>`, Body `{\"profileUrls\":[\"{{profile}}\"]}`, mapping the row's profile into the `profileUrls` array.\n\n**MCP.** In Claude, Cursor, or any MCP client with the Apify MCP server, ask for \"LinkedIn Profile API & Scraper Without Login or Cookies\" — the agent will find and run this actor.\n<!-- GENERATED:INTEGRATIONS:END -->\n\n## How to use\n\n<!-- GENERATED:HOWTO:START -->\n1. **In the Apify Console.** Open the actor page and click **Start** — the `profileUrls` field is already pre-filled with a working example. Results land in the run's dataset as soon as each item is found.\n2. **Via the API.** Call it directly with a POST request — no Console needed once you have an API token:\n   ```bash\n   curl \"https://api.apify.com/v2/acts/accountable_eel~linkedin-profile-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>\" \\\n     -X POST \\\n     -H \"Content-Type: application/json\" \\\n     -d '{\"profileUrls\":[\"https://www.linkedin.com/in/williamhgates\",\"satyanadella\"]}'\n   ```\n3. **On a schedule.** Save this actor as an Apify **Task** with the input you want, then add a **Schedule** (hourly, daily, weekly) so it runs on its own — no server of your own required.\n<!-- GENERATED:HOWTO:END -->\n\n1. Open the Input tab and paste your profile links into \"LinkedIn profiles to look up\", one per\n   line. A full link, a bare `linkedin.com/in/...`, or just the identifier after `/in/` all work.\n2. Leave \"Try it first\" on for the first run. It stops after 5 profiles so you can see the shape of\n   the output before spending on the whole list.\n3. Leave the proxy on residential. It is the default, and it is the single biggest factor in how\n   many of your profiles come back rather than blocked.\n4. Keep \"Max concurrency\" low, 2 to 3, on a long list. Speed here buys blocks, not throughput.\n5. Optionally narrow \"Which columns do you want?\", or use \"Only keep rows containing\" and \"Skip\n   rows containing\" to filter by keyword, for example to keep only profiles whose row mentions a\n   city or a job title.\n6. Turn \"Try it first\" off and press Start for the full list.\n\n**A bigger list in one run.** You don't have to run profiles one at a time — paste a whole\nshortlist into `profileUrls` and it processes in one job:\n\n```json\n{\n  \"profileUrls\": [\n    \"https://www.linkedin.com/in/williamhgates\",\n    \"https://www.linkedin.com/in/satyanadella\",\n    \"https://www.linkedin.com/in/reidhoffman\",\n    \"https://www.linkedin.com/in/melindagates\",\n    \"https://www.linkedin.com/in/jeffweiner08\",\n    \"https://www.linkedin.com/in/ariannahuffington\",\n    \"https://www.linkedin.com/in/simonsinek\",\n    \"https://www.linkedin.com/in/brenebrown\",\n    \"https://www.linkedin.com/in/adammgrant\",\n    \"https://www.linkedin.com/in/salliekrawcheck\",\n    \"https://www.linkedin.com/in/rbranson\",\n    \"https://www.linkedin.com/in/garyvaynerchuk\",\n    \"https://www.linkedin.com/in/barackobama\",\n    \"https://www.linkedin.com/in/jenhsunhuang\",\n    \"https://www.linkedin.com/in/bradsmi\",\n    \"https://www.linkedin.com/in/charleslamanna\",\n    \"https://www.linkedin.com/in/michaelabbott\",\n    \"https://www.linkedin.com/in/patmoorhead\",\n    \"https://www.linkedin.com/in/randi-weingarten-05896224\",\n    \"https://www.linkedin.com/in/tony-bates-679227a5\",\n    \"https://www.linkedin.com/in/charlesphillips1\",\n    \"https://www.linkedin.com/in/toufisaliba\",\n    \"https://www.linkedin.com/in/claire-hughes-johnson-7058\",\n    \"https://www.linkedin.com/in/lennyrachitsky\",\n    \"https://www.linkedin.com/in/binoyg\"\n  ]\n}\n```\n\n25 profiles ≈ **$0.20** if every one is found (25 × $0.008 per found row, plus the $0.00005 actor\nstart fee) — less for any that come back blocked or missing, since a miss is never charged.\n\n## Input\n\n<!-- GENERATED:INPUT:START -->\n```json\n{\n  \"profileUrls\": [\n    \"https://www.linkedin.com/in/williamhgates\",\n    \"satyanadella\"\n  ]\n}\n```\n\nOne profile per line. Paste the full profile link, or just the part after /in/. Accepted formats: https://www.linkedin.com/in/williamhgates, linkedin.com/in/williamhgates, williamhgates.\n<!-- GENERATED:INPUT:END -->\n\n## Output\n\nOne row per profile submitted:\n\n<!-- GENERATED:SAMPLE_OUTPUT:START -->\n| query | found | status | profileUrl | publicId | fullName | headline | location | currentTitle | currentCompany | currentCompanyUrl | experience | education | followers | connections | profileImageUrl | source | masked | changed | changeType | previousCompany | previousTitle | monitorFirstSeenAt | experience1Company | experience1Title | experience1Start | experience2Company | experience2Title | experience2Start | experience3Company | experience3Title | experience3Start | education1School | education1Degree | companyDomain | emailGuess | emailCandidates | emailDomainStatus | mxProvider | mailboxVerified | scrapedAt |\n| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |\n| reidhoffman | true | OK | https://www.linkedin.com/in/reidhoffman | reidhoffman | Reid Hoffman | Co-Founder, LinkedIn, Manas AI & Inflection AI. Founding Team, PayPal. Author of Superagency. Podcaster of Possible and Masters of Scale. | United States | Co-Founder, Executive Board Chair | Manas AI | https://www.linkedin.com/company/manas-ai | [{\"title\":\"Co-Founder, Executive Board Chair\",\"company\":\"Manas AI\",\"companyUrl\":\"https://www.linkedin.com/company/manas-ai\",\"location\":null,\"startDate\":\"2025-01\",\"endDate\":null},{\"title\":\"Co-Founde... | [{\"school\":\"Università degli Studi di Perugia\",\"degree\":null,\"startDate\":\"2024-05\",\"endDate\":\"2024-05\"},{\"school\":\"University of Oulu\",\"degree\":null,\"startDate\":\"2020\",\"endDate\":\"2020\"},{\"school\":\"... | 2796136 | 500+ | https://media.licdn.com/dms/image/v2/D5603AQHW7wKzPb3DAg/profile-displayphoto-shrink_200_200/profile-displayphoto-shrink_200_200/0/1718216204174 | jsonld | false |  |  |  |  |  | <role 1: company> | <role 1: title> | <role 1: start> | <role 2: company> | <role 2: title> | <role 2: start> | <role 3: company> | <role 3: title> | <role 3: start> | <school 1> | <school 1: degree> |  |  |  |  |  |  | 2026-10-09T06:01:17.079Z |\n<!-- GENERATED:SAMPLE_OUTPUT:END -->\n\nA profile that could not be read comes back as a row with `\"found\": false`, a `status`, and a\n`message` explaining which of the four reasons it was, and is never charged.\n\n## Tips\n\n- Run a handful of profiles you know by heart first. It is the fastest way to confirm the fields\n  land where you expect before you point a long list at it.\n- Filter on `masked: true` rather than trusting an empty `currentTitle`. A masked row is LinkedIn\n  trimming the page for that request, not the member leaving the field blank. If the headline\n  matters to you, treat a masked row as \"ask again later\", not as \"this person has no headline\".\n- `publicId` is the join key, not `fullName`. People change their display name and their headline;\n  the identifier after `/in/` is what stays the same.\n- A wave of `BLOCKED` rows means the addresses you are running from are being asked to sign in.\n  Lower \"Max concurrency\", leave residential proxy on, and re-run the blocked rows later. They cost\n  you nothing the first time.\n- Store `followers` as a number and `connections` as text. LinkedIn caps the connection display at\n  `500+`, so forcing it into a numeric column silently loses the distinction between 500 and 30,000.\n\n## vs. alternatives\n\n| | What it costs | What you get | Trade-off |\n|---|---|---|---|\n| **This actor** (`linkedin-profile-lookup`) | $0.008 per profile found at the FREE tier, less on paid tiers, $0.00005 actor start, nothing for a miss | One flat row per public profile: name, location, current company, current roles with dates, schools with dates, follower and connection counts, photo, and the headline and job title on the rows LinkedIn serves in full | Public profile pages only. Current roles, not full career history; no degree, field of study or skills, because the logged-out page does not publish them; and the headline and job title are missing whenever LinkedIn reduces the page, flagged by `masked`. Email guesses are pattern-based, not mailbox-verified. It looks up profiles you name; it does not search for people. |\n| **HarvestAPI's LinkedIn profile actors** | `linkedin-profile-search` is listed at $100 per 1,000 profiles | A whole family: profile lookup, people search, company and post coverage, and the category's most-used profile scraper at a 4.77 rating | If you need to *find* people by criteria rather than look up links you already hold, that is a search product and this is not one. |\n| **Clay** | $0.08 to $0.40 per enriched row in credits, on top of a seat | A whole enrichment workspace: waterfalls across dozens of providers, plus the table and the sequencing around it | If you want one place that does everything and you are not counting rows, that is Clay. This is one column, priced per column, callable *from* Clay via its HTTP step. |\n| Doing it yourself | Your time, plus residential proxy spend, plus keeping up with a sign-in wall that moves | The same fields | What this absorbs: the structured-data parsing, the reduced-page detection so asterisks never reach your table, the block-versus-missing-versus-bad-input distinction, and retrying a blocked address instead of failing the row. |\n\nPrices for third-party tools are their published list prices as of September 2026 and are not\ntracked here. Check the vendor before relying on the comparison.\n\n## FAQ\n\n**Do I need a LinkedIn account, or to paste in a cookie?**\nNo. That is the point of this actor. It reads the public profile page exactly as a signed-out\nbrowser gets it. You are never asked for credentials, a session cookie or a `li_at` token, and no\naccount of yours is put at risk by a run.\n\n**Why did a row come back with `found: false`?**\nOne of four reasons, and `status` says which. `BLOCKED`: LinkedIn asked that request to sign in\ninstead of serving the page. `NOT_FOUND`: no public profile exists at that address any more, or the\nmember has turned public visibility off. `BAD_FORMAT`: the input was not a member profile, most\noften a `/company/` page pasted by mistake. `REQUEST_FAILED`: the request did not complete after\nretries. None of the four is charged.\n\n**What is `masked: true`?**\nLinkedIn answers some requests with a reduced version of the profile in which job titles and past\nemployers are replaced by rows of asterisks. Rather than pass that through, the actor leaves those\nfields empty and sets `masked: true`. Name, location, current employer, follower and connection\ncounts, schools and photo still come through, so the row is worth having. How often it happens is\nLinkedIn's choice and it changes: on 2026-09-08 most fetches were reduced, and from 2026-09-09 to\n2026-09-16 none were (0 of 228 rows). It looks like a per-request decision rather than a property of\nthe profile or of where you run from, so re-running a masked row later is worth a try, and it costs\nyou a second charge for that profile.\n\n**Are the email addresses verified?**\nNo. `emailGuess` is guessed from the company's email pattern, not verified at the mailbox. The\nactor checks that the company domain can receive mail at all and ranks the usual address patterns\n(`first.last`, `first`, `firstlast`) by how common they are, but it never contacts a mail server,\nbecause cloud platforms block the port that would need. `mailboxVerified` is always `false` for\nthat reason. Run the guesses through a verification tool before a large send.\n\n**Why is `education[].degree` null?**\nBecause the logged-out profile page does not publish it. Degree and field of study are shown to\nsigned-in members only. The field is kept in the row so the output shape never changes between\nruns, but it is left null rather than filled from a guess.\n\n**Why is there no skills column?**\nBecause the logged-out page has no Skills section. None of the fifteen public profile fetches this\nactor was built against carried one, in either version of the page, so a skills column would be\nempty on every row. It is better to say that here than to ship the column.\n\n**Does it return full career history?**\nNo. The public page shows the roles a member presents as current, and that is what `experience`\ncontains. Everything earlier is behind the sign-in wall, and this actor does not go there.\n\n**Am I charged for a miss?**\nNo. Billing fires only when a real profile was read. Blocked rows, missing profiles and malformed\ninput all produce a row explaining themselves, unless you turn on \"Hide rows with no result\", and\nnone of them cost anything.\n\n**Do I need to configure a proxy?**\nIt is already configured. Residential proxy is the shipped default because LinkedIn's sign-in wall\nkeys on the reputation of the address asking. You can change the group in the Advanced section, but\nlowering it is the most common reason a list comes back mostly blocked.\n\n**Is this a live fetch or a stored database?**\nLive. Every run fetches the current page. Nothing is stored between runs, so a member who changed\njobs yesterday shows the new one today.\n\n**Can an AI agent call this directly?**\nYes. It is on the Apify MCP server, so an agent in Claude, Cursor or any other MCP client can find\nand run it by name, and the REST endpoint above works from any script or workflow tool.\n\n## Personal data, and your responsibility\n\nThis actor reads public LinkedIn profile pages, which are personal data about identifiable people.\nIt reads nothing behind the sign-in wall and it holds nothing between runs: each row is fetched\nlive and written to your own dataset, which only you control.\n\nWhether you may collect, store and use that data is a question about you and your purpose, not\nabout this tool. If you are in the EU or the UK, or your subjects are, the GDPR applies to what you\ndo next: you need a lawful basis, and duties around transparency, retention, and honouring a\nrequest to be erased. LinkedIn's own User Agreement also restricts automated collection, and it\nbinds anyone using it under an account. Nothing here is legal advice, and running this actor is not\na determination that your use is lawful. That call, and the compliance work behind it, is yours.\nA guessed work email is personal data too, and sending unsolicited email to one brings its own rules\n(for example the ePrivacy rules in the EU and the UK, and CAN-SPAM in the US).\n\n## Related actors\n\n\n- [LinkedIn Profile Posts Scraper: by URL, Without Login](https://apify.com/accountable_eel/linkedin-profile-posts-lookup): get a profile's latest posts by URL, without a login.\n\n- [LinkedIn Jobs Search Lookup](https://apify.com/accountable_eel/linkedin-jobs-search-lookup) run\n  a public LinkedIn job search by keyword and location and get one row per posting.\n- [Company Domain Enrichment](https://apify.com/accountable_eel/company-domain-enrichment) turn the\n  employer you just found into firmographics: registration, DNS, tech stack, hiring signal.\n- [Email Pattern Finder](https://apify.com/accountable_eel/email-pattern-finder) take the company\n  domain this actor found and work out that company's email pattern in more depth than the built-in\n  \"Guess each person's work email\" option here.\n- [Email Deliverability Check](https://apify.com/accountable_eel/email-deliverability-check) verify\n  the guessed address actually has a working mail route before you send to it.\n\nIf this saved you a scrape, a rating on the Store page helps other buyers find it.\n"
    },
    "buildNumber": "1.0.14",
    "imageDigest": "0140b0509dd42380eebb42bb67d2cc2496285d4df715a0a10527729e00ec88ae",
    "actVersion": {
      "sourceType": "SOURCE_FILES",
      "buildTag": "latest",
      "versionNumber": "1.0"
    },
    "usage": {
      "ACTOR_COMPUTE_UNITS": 0.03872777777777778
    }
  }
}