{
  "data": {
    "id": "ZVY9J0HQCrJ01NvFe",
    "actId": "upW63ob7bIqYTD9Zl",
    "userId": "ntHCnhNUkzXR7nbUZ",
    "startedAt": "2026-09-17T12:12:04.480Z",
    "finishedAt": "2026-09-17T12:12:06.474Z",
    "status": "SUCCEEDED",
    "meta": {
      "origin": "CLI"
    },
    "stats": {
      "durationMillis": 1868,
      "runTimeSecs": 1.868,
      "computeUnits": 0.0020755555555555557,
      "imageSizeBytes": 569874944
    },
    "options": {
      "useCache": true,
      "betaPackages": false,
      "memoryMbytes": 4096,
      "diskMbytes": 8192
    },
    "exitCode": 0,
    "inputSchema": "{\n  \"title\": \"LinkedIn Public People Profile Scraper Input\",\n  \"description\": \"Extract logged-out public professional profile fields from supplied LinkedIn profile URLs. LinkedIn may mask or omit fields that are not publicly exposed.\",\n  \"type\": \"object\",\n  \"schemaVersion\": 1,\n  \"required\": [\n    \"startUrls\"\n  ],\n  \"properties\": {\n    \"startUrls\": {\n      \"sectionCaption\": \"Profiles\",\n      \"sectionDescription\": \"Add known public LinkedIn person profile URLs to enrich.\",\n      \"title\": \"LinkedIn profile URLs\",\n      \"description\": \"Public linkedin.com/in/... profile URLs to enrich. Duplicate URLs are processed once.\",\n      \"type\": \"array\",\n      \"editor\": \"requestListSources\",\n      \"minItems\": 1,\n      \"maxItems\": 1000,\n      \"prefill\": [\n        {\n          \"url\": \"https://www.linkedin.com/in/williamhgates/\"\n        },\n        {\n          \"url\": \"https://www.linkedin.com/in/satyanadella/\"\n        }\n      ]\n    },\n    \"maxItems\": {\n      \"sectionCaption\": \"Run options\",\n      \"sectionDescription\": \"Set the maximum number of unique supplied profile URLs to process.\",\n      \"title\": \"Maximum profiles\",\n      \"description\": \"Maximum number of unique supplied profile URLs to process.\",\n      \"type\": \"integer\",\n      \"default\": 10,\n      \"prefill\": 10,\n      \"minimum\": 1,\n      \"maximum\": 1000\n    }\n  }\n}",
    "readme": "# LinkedIn Public People Profile Scraper\n\nTurn supplied **LinkedIn public profiles** into typed professional records without asking for LinkedIn credentials or cookies. Add known `linkedin.com/in/...` URLs and receive public identity, headline, location, employer, experience, education, audience, and source fields when LinkedIn exposes them to logged-out visitors.\n\nThis Actor is designed for recurring lead enrichment and workforce research. It does not search for people, log in to LinkedIn, or claim to reveal fields that LinkedIn masks.\n\n## What does LinkedIn Public People Profile Scraper do?\n\nFor each supplied public profile URL, the Actor:\n\n1. validates and normalizes the LinkedIn `/in/` URL;\n2. starts a bounded logged-out public web session;\n3. reads LinkedIn's structured public person metadata;\n4. removes values that LinkedIn replaces with asterisks;\n5. normalizes public career fields and keeps only school-shaped education records;\n6. saves one typed dataset row for each successful profile;\n7. charges a profile event only for a successfully saved profile.\n\nDuplicate input URLs are fetched once. Unavailable, private, deleted, or blocked profiles are logged and skipped. A run fails clearly if none of the supplied URLs produces a useful public profile.\n\n## Who is this LinkedIn profile scraper for?\n\n- **Sales operations teams** enriching known prospect profile URLs before CRM import.\n- **Recruiters and talent sourcers** adding public role, employer, location, and education context to candidate lists.\n- **Workforce researchers** comparing public professional attributes across a supplied cohort.\n- **RevOps analysts** normalizing mixed profile URL lists into consistent records.\n- **Data teams** feeding public professional metadata into spreadsheets, warehouses, and enrichment pipelines.\n\nUse a separate profile URL finder when you need discovery. This Actor starts from URLs you already have.\n\n## Why use this Actor?\n\n### No LinkedIn credentials\n\nThe input contains only public profile URLs. The Actor never requests a LinkedIn email, password, session cookie, or Sales Navigator account.\n\n### Typed, source-linked output\n\nEvery successful row contains canonical and fetched URLs plus an extraction timestamp. Experience and education are arrays rather than unstructured page text.\n\n### Honest logged-out visibility\n\nLinkedIn sometimes masks employers, titles, dates, skills, or education details. Masked values are omitted and a `warnings` message explains the limitation. LinkedIn can also place company or board affiliations in `alumniOf`; the Actor excludes entries that are neither typed as educational organizations nor linked to a LinkedIn `/school/` page and reports the omission in `warnings`. Empty arrays do not mean that the person has no experience, education, skills, or languages; they mean no qualifying fields were exposed through the supported public surface.\n\n### Batch-safe behavior\n\nThe Actor validates every URL, deduplicates the supplied list, respects `maxItems`, uses bounded retries, and stops with a non-zero failure when no useful result is available.\n\n## What LinkedIn public profile data is extracted?\n\n| Field | Type | Meaning |\n| --- | --- | --- |\n| `fullName` | string | Public profile name |\n| `headline` | string or null | Public professional headline |\n| `location` | string or null | Public profile location |\n| `countryCode` | string or null | Public country code when exposed |\n| `currentEmployer` | string or null | First publicly exposed employer |\n| `about` | string or null | Public profile summary |\n| `experience` | array | Public title, company, company URL, and dates when exposed |\n| `education` | array | Public school institution, URL, degree, and years when exposed; non-school `alumniOf` records are excluded |\n| `skills` | array | Skills exposed in public structured profile data |\n| `languages` | array | Public languages when exposed |\n| `followerCount` | number or null | Public follower count |\n| `profileImageUrl` | string or null | Public profile image URL |\n| `linkedinUrl` | string | Canonical profile URL returned by LinkedIn |\n| `sourceUrl` | string | Normalized URL requested by the Actor |\n| `scrapedAt` | string | ISO 8601 observation timestamp |\n| `warnings` | array | Notices about masked public values |\n\n## How much does it cost to extract LinkedIn public profiles?\n\nThe Actor uses pay-per-event pricing:\n\n- one `start` event at **$0.005** for each run;\n- one `profile` event for each successfully extracted and saved public profile.\n\n| Apify plan | Run start | Per successful profile |\n| --- | ---: | ---: |\n| Free | $0.005 | $0.00460 |\n| Bronze | $0.005 | $0.00400 |\n| Silver | $0.005 | $0.00312 |\n| Gold | $0.005 | $0.00240 |\n| Platinum | $0.005 | $0.00160 |\n| Diamond | $0.005 | $0.00112 |\n\nFailed, unavailable, duplicate, or rejected URLs do not produce a profile event. On the Bronze plan, 1 successful profile costs **$0.009**, 25 cost **$0.105**, and 100 cost **$0.405**, including the run-start event.\n\nAs a usage example, a run with 25 supplied URLs and 22 successful public profiles costs **$0.093** on Bronze. Batch URLs in one run when practical so that you pay the start event once.\n\n## Getting started\n\n1. Open the Actor input page in Apify Console.\n2. Add one or more public `https://www.linkedin.com/in/...` URLs.\n3. Set **Maximum profiles** to the largest number you want processed.\n4. Click **Start**.\n5. Open the default dataset.\n6. Review `warnings` before using career fields in a workflow.\n7. Export the rows as JSON, CSV, Excel, XML, or RSS, or fetch them through the Dataset API.\n\n## Input parameters\n\n### LinkedIn profile URLs (`startUrls`)\n\nRequired. Add between 1 and 1,000 public LinkedIn `/in/` profile URLs.\n\n```json\n{\n  \"startUrls\": [\n    { \"url\": \"https://www.linkedin.com/in/williamhgates/\" },\n    { \"url\": \"https://www.linkedin.com/in/satyanadella/\" }\n  ]\n}\n```\n\nThe Actor accepts the request-list-source format used by Apify's URL editor. Other domains, company pages, job pages, search pages, and malformed URLs fail validation.\n\n### Maximum profiles (`maxItems`)\n\nOptional integer from 1 to 1,000. The default is 10. Processing considers at most this many unique normalized input URLs.\n\n```json\n{\n  \"startUrls\": [\n    { \"url\": \"https://www.linkedin.com/in/satyanadella/\" },\n    { \"url\": \"https://www.linkedin.com/in/satyanadella/\" },\n    { \"url\": \"https://www.linkedin.com/in/jeffweiner08/\" }\n  ],\n  \"maxItems\": 2\n}\n```\n\nThe duplicate Satya Nadella URL is fetched once.\n\n## Output example\n\nA current logged-out public result has this shape:\n\n```json\n{\n  \"fullName\": \"Bill Gates\",\n  \"headline\": \"Gates Foundation\",\n  \"location\": \"Seattle, Washington, United States\",\n  \"countryCode\": \"US\",\n  \"currentEmployer\": \"Gates Foundation\",\n  \"about\": \"Chair of the Gates Foundation. Founder of Breakthrough Energy. Co-founder of Microsoft…\",\n  \"experience\": [\n    {\n      \"title\": null,\n      \"company\": \"Gates Foundation\",\n      \"companyUrl\": \"https://www.linkedin.com/company/gates-foundation\",\n      \"startDate\": null,\n      \"endDate\": null\n    }\n  ],\n  \"education\": [\n    {\n      \"institution\": \"Harvard University\",\n      \"institutionUrl\": \"https://www.linkedin.com/school/harvard-university/\",\n      \"degree\": null,\n      \"startYear\": 1973,\n      \"endYear\": 1975\n    }\n  ],\n  \"skills\": [],\n  \"languages\": [],\n  \"followerCount\": 40601541,\n  \"profileImageUrl\": \"https://media.licdn.com/dms/image/...\",\n  \"linkedinUrl\": \"https://www.linkedin.com/in/williamhgates\",\n  \"sourceUrl\": \"https://www.linkedin.com/in/williamhgates/\",\n  \"scrapedAt\": \"2026-08-26T06:36:19.398Z\",\n  \"warnings\": [\n    \"LinkedIn masked one or more logged-out experience fields; masked values were omitted.\"\n  ]\n}\n```\n\nPublic values, counts, image URLs, and visibility change over time. Treat this as the output shape, not a permanent snapshot of the person.\n\n## Build a lead-enrichment workflow\n\nA practical recurring workflow is:\n\n1. collect profile URLs through a consented CRM, event list, or public search workflow;\n2. deduplicate URLs in your source system;\n3. run this Actor on the known URLs;\n4. join results back on `sourceUrl` or `linkedinUrl`;\n5. segment by public headline, location, or employer;\n6. review warnings and null fields instead of treating them as negative facts;\n7. schedule a later run only when you have a lawful need to refresh public observations.\n\nThe Actor does not perform outreach or infer contact details.\n\n## Run workforce research responsibly\n\nFor aggregate research:\n\n- define the cohort before running the Actor;\n- retain `scrapedAt` with each observation;\n- distinguish `null` and empty arrays from confirmed absence;\n- avoid conclusions based on a field LinkedIn masks selectively;\n- aggregate where individual identification is unnecessary;\n- keep only the data needed for the research purpose.\n\n## Schedule recurring profile enrichment\n\nApify schedules can rerun the same task daily, weekly, or monthly. Use recurring runs only when changes matter to your workflow. Store snapshots in your own destination if you need comparisons; this Actor returns the current public observation and does not create alerts or compare historical runs itself.\n\nUseful patterns include:\n\n- refresh known account executives before quarterly territory planning;\n- update a consented recruiting pipeline weekly;\n- observe a defined public leadership cohort monthly;\n- trigger a downstream CRM import through a webhook after success.\n\n## Export to spreadsheets and data pipelines\n\nThe default dataset works with:\n\n- JSON and JSONL for services and warehouses;\n- CSV or Excel for analysts and recruiters;\n- the Apify Google Sheets integration;\n- webhooks for downstream automation;\n- Make, Zapier, and other Apify integrations;\n- Dataset API reads from Node.js, Python, or any HTTP client.\n\nNested `experience` and `education` arrays are preserved in JSON. For flat spreadsheet workflows, transform those arrays downstream or select the top-level `currentEmployer` field.\n\n## API usage with cURL\n\nStart a run and wait for completion:\n\n```bash\ncurl -X POST \\\n  \"https://api.apify.com/v2/acts/automation-lab~linkedin-public-people-profile-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"startUrls\": [\n      {\"url\": \"https://www.linkedin.com/in/williamhgates/\"}\n    ],\n    \"maxItems\": 1\n  }'\n```\n\nKeep your Apify token in a secret manager. Do not place it in source code or a public task.\n\n## API usage with JavaScript\n\n```javascript\nimport { ApifyClient } from 'apify-client';\n\nconst client = new ApifyClient({ token: process.env.APIFY_TOKEN });\nconst run = await client.actor(\n  'automation-lab/linkedin-public-people-profile-scraper'\n).call({\n  startUrls: [\n    { url: 'https://www.linkedin.com/in/satyanadella/' },\n    { url: 'https://www.linkedin.com/in/jeffweiner08/' },\n  ],\n  maxItems: 2,\n});\n\nconst { items } = await client.dataset(run.defaultDatasetId).listItems();\nfor (const profile of items) {\n  console.log(profile.fullName, profile.currentEmployer, profile.linkedinUrl);\n}\n```\n\n## API usage with Python\n\n```python\nimport os\nfrom apify_client import ApifyClient\n\nclient = ApifyClient(os.environ[\"APIFY_TOKEN\"])\nrun = client.actor(\n    \"automation-lab/linkedin-public-people-profile-scraper\"\n).call(run_input={\n    \"startUrls\": [\n        {\"url\": \"https://www.linkedin.com/in/williamhgates/\"}\n    ],\n    \"maxItems\": 1,\n})\n\nfor profile in client.dataset(run[\"defaultDatasetId\"]).iterate_items():\n    print(profile[\"fullName\"], profile.get(\"headline\"))\n```\n\n## Use with Apify MCP\n\nAdd the Actor to Claude Code:\n\n```bash\nclaude mcp add --transport http apify \\\n  \"https://mcp.apify.com?tools=automation-lab/linkedin-public-people-profile-scraper\"\n```\n\n**Claude Desktop setup:** add this server object to `claude_desktop_config.json`.\n\n**Cursor setup:** add the same server object under MCP settings.\n\n**VS Code setup:** add the same HTTP server URL to the editor's MCP server configuration.\n\n```json\n{\n  \"mcpServers\": {\n    \"apify\": {\n      \"url\": \"https://mcp.apify.com?tools=automation-lab/linkedin-public-people-profile-scraper\"\n    }\n  }\n}\n```\n\nExample prompts:\n\n- “Run the LinkedIn Public People Profile Scraper for these three public profile URLs and return name, location, employer, and warnings.”\n- “Enrich this supplied executive profile list and export the successful public rows as CSV.”\n- “Summarize the publicly exposed education fields for this defined LinkedIn profile cohort.”\n\n## Limits and expected failure behavior\n\n- Only public LinkedIn `/in/` URLs are supported.\n- LinkedIn decides which logged-out fields are exposed.\n- Masked asterisk values are discarded.\n- Private, deleted, invalid, or unavailable profiles produce no dataset row.\n- A run can succeed with fewer rows than supplied URLs when some profiles are unavailable.\n- A run fails when every supplied profile fails extraction.\n- The Actor does not log in, solve CAPTCHAs, bypass profile privacy, search LinkedIn, extract emails, download media files, or retrieve private connection data.\n- Public page structure and visibility can change without notice.\n\n## Troubleshooting\n\n### Why is experience or education incomplete?\n\nLinkedIn often shows only part of a profile to logged-out visitors and may replace values with asterisks. It can also mix company or board affiliations into `alumniOf`; those records are excluded unless they have an educational type or LinkedIn `/school/` URL. Check `warnings`. The Actor intentionally returns only qualifying fields exposed through the supported public surface.\n\n### Why did I receive fewer rows than URLs?\n\nReview the run log for skipped URLs. Common causes are a private or deleted profile, a malformed `/in/` URL, temporary LinkedIn blocking, or no public Person metadata. Retry temporary failures later, but do not expect the Actor to reveal private profiles.\n\n### Why did the entire run fail?\n\nThe Actor fails if no useful profile was extracted. Check that the input uses public `linkedin.com/in/...` URLs and test one known public profile before retrying a large batch.\n\n### Does an empty array mean the person has none?\n\nNo. An empty `skills`, `languages`, `experience`, or `education` array means no entries were exposed through the logged-out structured surface at extraction time.\n\n## Tips for reliable runs\n\n- Prefer canonical public profile URLs copied from LinkedIn.\n- Put multiple URLs into one run instead of starting one run per profile.\n- Keep `maxItems` close to the intended batch size.\n- Deduplicate upstream as well as relying on Actor deduplication.\n- Preserve `warnings` in downstream systems.\n- Use `scrapedAt` when comparing observations.\n- Do not use a temporary field omission as evidence that a person left a role or institution.\n\n## Legal and responsible use\n\nScrape only public data you are permitted to process. Follow applicable laws, contractual obligations, LinkedIn's terms, and Apify's policies. Establish a lawful purpose, minimize collected fields, secure exports, honor deletion or access rights where applicable, and avoid sensitive decisions based solely on scraped data.\n\nThis Actor is a technical data-extraction tool, not legal advice. You are responsible for your inputs, processing purpose, retention, and downstream use.\n\n## Related automation-lab Actors\n\n- [LinkedIn Profile URL Finder](https://apify.com/automation-lab/linkedin-profile-url-finder) discovers likely public profile URLs before enrichment.\n- [LinkedIn Public Profile Search Scraper](https://apify.com/automation-lab/linkedin-public-profile-search-scraper) returns public profile search result cards.\n- [LinkedIn Company Employees Scraper](https://apify.com/automation-lab/linkedin-company-employees-scraper) supports company-centered workforce discovery.\n- [LinkedIn Profile Contact Enricher](https://apify.com/automation-lab/linkedin-profile-contact-enricher) adds source-attributed public company contact context when you supply official domains.\n\nChoose this Actor when you already have public person profile URLs and need normalized public professional records.\n\n## FAQ\n\n### Does it require a LinkedIn account?\n\nNo. It uses logged-out public surfaces and Apify Proxy sessions.\n\n### Can I provide profile slugs instead of URLs?\n\nNo. Full public `/in/` URLs make scope explicit and reduce ambiguity.\n\n### Does it find emails or phone numbers?\n\nNo. It extracts professional profile fields exposed publicly by LinkedIn.\n\n### Does it search for people?\n\nNo. Supply known public profile URLs. Use a related URL finder or public profile search Actor for discovery.\n\n### Are all fields guaranteed?\n\nNo. `fullName` is required for a successful row; every other public field can be null or empty when LinkedIn does not expose it.\n\n### Can I schedule it?\n\nYes. Use an Apify Task and schedule, then send the dataset to your chosen destination.\n\n### Is each failed URL charged as a profile?\n\nNo. The `profile` event is emitted only after a useful profile has been parsed and before its row is saved.\n",
    "changelog": "# Changelog\n\n## Next\n\n- Improved education accuracy by excluding company and board-affiliation records that LinkedIn can place in profile `alumniOf` metadata, while preserving genuine school entries.\n- Added an output warning when non-school or unusable `alumniOf` records are omitted.\n\n## 0.1.0\n\n- Added the initial LinkedIn Public People Profile Scraper input, output, and pricing contracts.\n",
    "actorDefinition": {
      "actorSpecification": 1,
      "name": "linkedin-public-people-profile-scraper",
      "title": "LinkedIn Public People Profile Scraper",
      "description": "Extract public identity, headline, location, employer, experience, and education fields from supplied logged-out LinkedIn profile URLs.",
      "version": "0.1",
      "buildTag": "latest",
      "meta": {
        "templateId": "automation-labs-bootstrap-http",
        "generatedBy": "APIA-2590 bootstrap-actor"
      },
      "input": {
        "title": "LinkedIn Public People Profile Scraper Input",
        "description": "Extract logged-out public professional profile fields from supplied LinkedIn profile URLs. LinkedIn may mask or omit fields that are not publicly exposed.",
        "type": "object",
        "schemaVersion": 1,
        "required": [
          "startUrls"
        ],
        "properties": {
          "startUrls": {
            "sectionCaption": "Profiles",
            "sectionDescription": "Add known public LinkedIn person profile URLs to enrich.",
            "title": "LinkedIn profile URLs",
            "description": "Public linkedin.com/in/... profile URLs to enrich. Duplicate URLs are processed once.",
            "type": "array",
            "editor": "requestListSources",
            "minItems": 1,
            "maxItems": 1000,
            "prefill": [
              {
                "url": "https://www.linkedin.com/in/williamhgates/"
              },
              {
                "url": "https://www.linkedin.com/in/satyanadella/"
              }
            ]
          },
          "maxItems": {
            "sectionCaption": "Run options",
            "sectionDescription": "Set the maximum number of unique supplied profile URLs to process.",
            "title": "Maximum profiles",
            "description": "Maximum number of unique supplied profile URLs to process.",
            "type": "integer",
            "default": 10,
            "prefill": 10,
            "minimum": 1,
            "maximum": 1000
          }
        }
      },
      "output": {
        "actorOutputSchemaVersion": 1,
        "title": "LinkedIn public profile results",
        "description": "Dataset containing public identity and professional profile fields extracted from supplied LinkedIn URLs.",
        "properties": {
          "dataset": {
            "type": "string",
            "title": "LinkedIn public profiles",
            "description": "Default dataset containing successfully extracted public profiles.",
            "template": "{{links.apiDefaultDatasetUrl}}/items?view=overview"
          }
        }
      },
      "changelog": "# Changelog\n\n## Next\n\n- Improved education accuracy by excluding company and board-affiliation records that LinkedIn can place in profile `alumniOf` metadata, while preserving genuine school entries.\n- Added an output warning when non-school or unusable `alumniOf` records are omitted.\n\n## 0.1.0\n\n- Added the initial LinkedIn Public People Profile Scraper input, output, and pricing contracts.\n",
      "dockerfile": "../Dockerfile",
      "storages": {
        "dataset": {
          "actorSpecification": 1,
          "fields": {
            "$schema": "http://json-schema.org/draft-07/schema#",
            "type": "object",
            "properties": {
              "fullName": {
                "type": "string",
                "description": "Public profile name.",
                "nullable": true,
                "example": "Sample Professional"
              },
              "headline": {
                "type": "string",
                "description": "Public professional headline.",
                "nullable": true,
                "example": "Product leader at Sample Company"
              },
              "location": {
                "type": "string",
                "description": "Public profile location.",
                "nullable": true,
                "example": "Seattle, Washington, United States"
              },
              "countryCode": {
                "type": "string",
                "description": "Public two-letter country code when exposed.",
                "nullable": true,
                "example": "US"
              },
              "currentEmployer": {
                "type": "string",
                "description": "First publicly exposed current employer.",
                "nullable": true,
                "example": "Sample Company"
              },
              "about": {
                "type": "string",
                "description": "Public profile summary.",
                "nullable": true,
                "example": "Building useful products and teams."
              },
              "experience": {
                "type": "array",
                "description": "Public experience entries exposed to logged-out visitors.",
                "nullable": true,
                "example": [
                  {
                    "title": "Product Lead",
                    "company": "Sample Company",
                    "companyUrl": "https://www.linkedin.com/company/sample-company",
                    "startDate": "2022",
                    "endDate": null
                  }
                ],
                "items": {
                  "type": "object",
                  "properties": {
                    "title": {
                      "type": "string",
                      "description": "Public role title.",
                      "nullable": true,
                      "example": "Product Lead"
                    },
                    "company": {
                      "type": "string",
                      "description": "Public employer name.",
                      "nullable": true,
                      "example": "Sample Company"
                    },
                    "companyUrl": {
                      "type": "string",
                      "description": "LinkedIn company URL when exposed.",
                      "nullable": true,
                      "example": "https://www.linkedin.com/company/sample-company"
                    },
                    "startDate": {
                      "type": "string",
                      "description": "Public role start date when exposed.",
                      "nullable": true,
                      "example": "2022"
                    },
                    "endDate": {
                      "type": "string",
                      "description": "Public role end date when exposed.",
                      "nullable": true,
                      "example": "2025"
                    }
                  },
                  "required": [],
                  "additionalProperties": true
                }
              },
              "education": {
                "type": "array",
                "description": "Public education entries exposed to logged-out visitors.",
                "nullable": true,
                "example": [
                  {
                    "institution": "Sample University",
                    "institutionUrl": "https://www.linkedin.com/school/sample-university",
                    "degree": "Example degree",
                    "startYear": 2014,
                    "endYear": 2018
                  }
                ],
                "items": {
                  "type": "object",
                  "properties": {
                    "institution": {
                      "type": "string",
                      "description": "Public institution name.",
                      "nullable": true,
                      "example": "Sample University"
                    },
                    "institutionUrl": {
                      "type": "string",
                      "description": "LinkedIn institution URL when exposed.",
                      "nullable": true,
                      "example": "https://www.linkedin.com/school/sample-university"
                    },
                    "degree": {
                      "type": "string",
                      "description": "Public degree description when exposed.",
                      "nullable": true,
                      "example": "Example degree"
                    },
                    "startYear": {
                      "type": "integer",
                      "description": "Public education start year.",
                      "nullable": true,
                      "example": 2014
                    },
                    "endYear": {
                      "type": "integer",
                      "description": "Public education end year.",
                      "nullable": true,
                      "example": 2018
                    }
                  },
                  "required": [],
                  "additionalProperties": true
                }
              },
              "skills": {
                "type": "array",
                "description": "Public skills when exposed in structured profile data.",
                "items": {
                  "type": "string"
                },
                "nullable": true,
                "example": [
                  "Product strategy"
                ]
              },
              "languages": {
                "type": "array",
                "description": "Public languages when exposed.",
                "items": {
                  "type": "string"
                },
                "nullable": true,
                "example": [
                  "English"
                ]
              },
              "followerCount": {
                "type": "integer",
                "description": "Public follower count when exposed.",
                "nullable": true,
                "example": 12500
              },
              "profileImageUrl": {
                "type": "string",
                "description": "Public profile image URL.",
                "nullable": true,
                "example": "https://media.licdn.com/dms/image/example/profile-displayphoto"
              },
              "linkedinUrl": {
                "type": "string",
                "description": "Canonical LinkedIn profile URL.",
                "nullable": true,
                "example": "https://www.linkedin.com/in/sample-professional"
              },
              "sourceUrl": {
                "type": "string",
                "description": "Normalized supplied URL fetched for this record.",
                "nullable": true,
                "example": "https://www.linkedin.com/in/sample-professional/"
              },
              "scrapedAt": {
                "type": "string",
                "description": "ISO 8601 extraction timestamp.",
                "format": "date-time",
                "nullable": true,
                "example": "2025-01-15T12:00:00.000Z"
              },
              "warnings": {
                "type": "array",
                "description": "Notices about public fields LinkedIn masked or omitted.",
                "items": {
                  "type": "string"
                },
                "nullable": true,
                "example": [
                  "LinkedIn masked one or more logged-out experience fields; masked values were omitted."
                ]
              }
            },
            "required": [],
            "additionalProperties": true
          },
          "views": {
            "overview": {
              "title": "LinkedIn public profiles",
              "description": "Identity, role, location, employer, and public career context.",
              "transformation": {
                "fields": [
                  "fullName",
                  "headline",
                  "location",
                  "currentEmployer",
                  "experience",
                  "education",
                  "followerCount",
                  "linkedinUrl",
                  "scrapedAt",
                  "warnings"
                ]
              },
              "display": {
                "component": "table",
                "properties": {
                  "fullName": {
                    "label": "Name",
                    "format": "text"
                  },
                  "headline": {
                    "label": "Headline",
                    "format": "text"
                  },
                  "location": {
                    "label": "Location",
                    "format": "text"
                  },
                  "currentEmployer": {
                    "label": "Current employer",
                    "format": "text"
                  },
                  "experience": {
                    "label": "Experience",
                    "format": "array"
                  },
                  "education": {
                    "label": "Education",
                    "format": "array"
                  },
                  "followerCount": {
                    "label": "Followers",
                    "format": "number"
                  },
                  "linkedinUrl": {
                    "label": "LinkedIn profile",
                    "format": "link"
                  },
                  "scrapedAt": {
                    "label": "Scraped at",
                    "format": "date"
                  },
                  "warnings": {
                    "label": "Warnings",
                    "format": "array"
                  }
                }
              }
            }
          }
        }
      },
      "defaultRunOptions": {
        "timeoutSecs": 300,
        "memoryMbytes": 256,
        "build": "latest"
      },
      "actorChargeEvents": {
        "start": {
          "eventTitle": "Run started",
          "eventDescription": "One-time fee charged when a run starts. Covers fixed startup cost (init, proxy warmup, first HTTP setup).",
          "eventPriceUsd": 0.005,
          "isOneTimeEvent": true
        },
        "profile": {
          "eventTitle": "Profile extracted",
          "eventDescription": "Charged per profile extracted.",
          "isPrimaryEvent": true,
          "eventTieredPricingUsd": {
            "FREE": {
              "tieredEventPriceUsd": 0.0046
            },
            "BRONZE": {
              "tieredEventPriceUsd": 0.004
            },
            "SILVER": {
              "tieredEventPriceUsd": 0.00312
            },
            "GOLD": {
              "tieredEventPriceUsd": 0.0024
            },
            "PLATINUM": {
              "tieredEventPriceUsd": 0.0016
            },
            "DIAMOND": {
              "tieredEventPriceUsd": 0.00112
            }
          }
        }
      },
      "readme": "# LinkedIn Public People Profile Scraper\n\nTurn supplied **LinkedIn public profiles** into typed professional records without asking for LinkedIn credentials or cookies. Add known `linkedin.com/in/...` URLs and receive public identity, headline, location, employer, experience, education, audience, and source fields when LinkedIn exposes them to logged-out visitors.\n\nThis Actor is designed for recurring lead enrichment and workforce research. It does not search for people, log in to LinkedIn, or claim to reveal fields that LinkedIn masks.\n\n## What does LinkedIn Public People Profile Scraper do?\n\nFor each supplied public profile URL, the Actor:\n\n1. validates and normalizes the LinkedIn `/in/` URL;\n2. starts a bounded logged-out public web session;\n3. reads LinkedIn's structured public person metadata;\n4. removes values that LinkedIn replaces with asterisks;\n5. normalizes public career fields and keeps only school-shaped education records;\n6. saves one typed dataset row for each successful profile;\n7. charges a profile event only for a successfully saved profile.\n\nDuplicate input URLs are fetched once. Unavailable, private, deleted, or blocked profiles are logged and skipped. A run fails clearly if none of the supplied URLs produces a useful public profile.\n\n## Who is this LinkedIn profile scraper for?\n\n- **Sales operations teams** enriching known prospect profile URLs before CRM import.\n- **Recruiters and talent sourcers** adding public role, employer, location, and education context to candidate lists.\n- **Workforce researchers** comparing public professional attributes across a supplied cohort.\n- **RevOps analysts** normalizing mixed profile URL lists into consistent records.\n- **Data teams** feeding public professional metadata into spreadsheets, warehouses, and enrichment pipelines.\n\nUse a separate profile URL finder when you need discovery. This Actor starts from URLs you already have.\n\n## Why use this Actor?\n\n### No LinkedIn credentials\n\nThe input contains only public profile URLs. The Actor never requests a LinkedIn email, password, session cookie, or Sales Navigator account.\n\n### Typed, source-linked output\n\nEvery successful row contains canonical and fetched URLs plus an extraction timestamp. Experience and education are arrays rather than unstructured page text.\n\n### Honest logged-out visibility\n\nLinkedIn sometimes masks employers, titles, dates, skills, or education details. Masked values are omitted and a `warnings` message explains the limitation. LinkedIn can also place company or board affiliations in `alumniOf`; the Actor excludes entries that are neither typed as educational organizations nor linked to a LinkedIn `/school/` page and reports the omission in `warnings`. Empty arrays do not mean that the person has no experience, education, skills, or languages; they mean no qualifying fields were exposed through the supported public surface.\n\n### Batch-safe behavior\n\nThe Actor validates every URL, deduplicates the supplied list, respects `maxItems`, uses bounded retries, and stops with a non-zero failure when no useful result is available.\n\n## What LinkedIn public profile data is extracted?\n\n| Field | Type | Meaning |\n| --- | --- | --- |\n| `fullName` | string | Public profile name |\n| `headline` | string or null | Public professional headline |\n| `location` | string or null | Public profile location |\n| `countryCode` | string or null | Public country code when exposed |\n| `currentEmployer` | string or null | First publicly exposed employer |\n| `about` | string or null | Public profile summary |\n| `experience` | array | Public title, company, company URL, and dates when exposed |\n| `education` | array | Public school institution, URL, degree, and years when exposed; non-school `alumniOf` records are excluded |\n| `skills` | array | Skills exposed in public structured profile data |\n| `languages` | array | Public languages when exposed |\n| `followerCount` | number or null | Public follower count |\n| `profileImageUrl` | string or null | Public profile image URL |\n| `linkedinUrl` | string | Canonical profile URL returned by LinkedIn |\n| `sourceUrl` | string | Normalized URL requested by the Actor |\n| `scrapedAt` | string | ISO 8601 observation timestamp |\n| `warnings` | array | Notices about masked public values |\n\n## How much does it cost to extract LinkedIn public profiles?\n\nThe Actor uses pay-per-event pricing:\n\n- one `start` event at **$0.005** for each run;\n- one `profile` event for each successfully extracted and saved public profile.\n\n| Apify plan | Run start | Per successful profile |\n| --- | ---: | ---: |\n| Free | $0.005 | $0.00460 |\n| Bronze | $0.005 | $0.00400 |\n| Silver | $0.005 | $0.00312 |\n| Gold | $0.005 | $0.00240 |\n| Platinum | $0.005 | $0.00160 |\n| Diamond | $0.005 | $0.00112 |\n\nFailed, unavailable, duplicate, or rejected URLs do not produce a profile event. On the Bronze plan, 1 successful profile costs **$0.009**, 25 cost **$0.105**, and 100 cost **$0.405**, including the run-start event.\n\nAs a usage example, a run with 25 supplied URLs and 22 successful public profiles costs **$0.093** on Bronze. Batch URLs in one run when practical so that you pay the start event once.\n\n## Getting started\n\n1. Open the Actor input page in Apify Console.\n2. Add one or more public `https://www.linkedin.com/in/...` URLs.\n3. Set **Maximum profiles** to the largest number you want processed.\n4. Click **Start**.\n5. Open the default dataset.\n6. Review `warnings` before using career fields in a workflow.\n7. Export the rows as JSON, CSV, Excel, XML, or RSS, or fetch them through the Dataset API.\n\n## Input parameters\n\n### LinkedIn profile URLs (`startUrls`)\n\nRequired. Add between 1 and 1,000 public LinkedIn `/in/` profile URLs.\n\n```json\n{\n  \"startUrls\": [\n    { \"url\": \"https://www.linkedin.com/in/williamhgates/\" },\n    { \"url\": \"https://www.linkedin.com/in/satyanadella/\" }\n  ]\n}\n```\n\nThe Actor accepts the request-list-source format used by Apify's URL editor. Other domains, company pages, job pages, search pages, and malformed URLs fail validation.\n\n### Maximum profiles (`maxItems`)\n\nOptional integer from 1 to 1,000. The default is 10. Processing considers at most this many unique normalized input URLs.\n\n```json\n{\n  \"startUrls\": [\n    { \"url\": \"https://www.linkedin.com/in/satyanadella/\" },\n    { \"url\": \"https://www.linkedin.com/in/satyanadella/\" },\n    { \"url\": \"https://www.linkedin.com/in/jeffweiner08/\" }\n  ],\n  \"maxItems\": 2\n}\n```\n\nThe duplicate Satya Nadella URL is fetched once.\n\n## Output example\n\nA current logged-out public result has this shape:\n\n```json\n{\n  \"fullName\": \"Bill Gates\",\n  \"headline\": \"Gates Foundation\",\n  \"location\": \"Seattle, Washington, United States\",\n  \"countryCode\": \"US\",\n  \"currentEmployer\": \"Gates Foundation\",\n  \"about\": \"Chair of the Gates Foundation. Founder of Breakthrough Energy. Co-founder of Microsoft…\",\n  \"experience\": [\n    {\n      \"title\": null,\n      \"company\": \"Gates Foundation\",\n      \"companyUrl\": \"https://www.linkedin.com/company/gates-foundation\",\n      \"startDate\": null,\n      \"endDate\": null\n    }\n  ],\n  \"education\": [\n    {\n      \"institution\": \"Harvard University\",\n      \"institutionUrl\": \"https://www.linkedin.com/school/harvard-university/\",\n      \"degree\": null,\n      \"startYear\": 1973,\n      \"endYear\": 1975\n    }\n  ],\n  \"skills\": [],\n  \"languages\": [],\n  \"followerCount\": 40601541,\n  \"profileImageUrl\": \"https://media.licdn.com/dms/image/...\",\n  \"linkedinUrl\": \"https://www.linkedin.com/in/williamhgates\",\n  \"sourceUrl\": \"https://www.linkedin.com/in/williamhgates/\",\n  \"scrapedAt\": \"2026-08-26T06:36:19.398Z\",\n  \"warnings\": [\n    \"LinkedIn masked one or more logged-out experience fields; masked values were omitted.\"\n  ]\n}\n```\n\nPublic values, counts, image URLs, and visibility change over time. Treat this as the output shape, not a permanent snapshot of the person.\n\n## Build a lead-enrichment workflow\n\nA practical recurring workflow is:\n\n1. collect profile URLs through a consented CRM, event list, or public search workflow;\n2. deduplicate URLs in your source system;\n3. run this Actor on the known URLs;\n4. join results back on `sourceUrl` or `linkedinUrl`;\n5. segment by public headline, location, or employer;\n6. review warnings and null fields instead of treating them as negative facts;\n7. schedule a later run only when you have a lawful need to refresh public observations.\n\nThe Actor does not perform outreach or infer contact details.\n\n## Run workforce research responsibly\n\nFor aggregate research:\n\n- define the cohort before running the Actor;\n- retain `scrapedAt` with each observation;\n- distinguish `null` and empty arrays from confirmed absence;\n- avoid conclusions based on a field LinkedIn masks selectively;\n- aggregate where individual identification is unnecessary;\n- keep only the data needed for the research purpose.\n\n## Schedule recurring profile enrichment\n\nApify schedules can rerun the same task daily, weekly, or monthly. Use recurring runs only when changes matter to your workflow. Store snapshots in your own destination if you need comparisons; this Actor returns the current public observation and does not create alerts or compare historical runs itself.\n\nUseful patterns include:\n\n- refresh known account executives before quarterly territory planning;\n- update a consented recruiting pipeline weekly;\n- observe a defined public leadership cohort monthly;\n- trigger a downstream CRM import through a webhook after success.\n\n## Export to spreadsheets and data pipelines\n\nThe default dataset works with:\n\n- JSON and JSONL for services and warehouses;\n- CSV or Excel for analysts and recruiters;\n- the Apify Google Sheets integration;\n- webhooks for downstream automation;\n- Make, Zapier, and other Apify integrations;\n- Dataset API reads from Node.js, Python, or any HTTP client.\n\nNested `experience` and `education` arrays are preserved in JSON. For flat spreadsheet workflows, transform those arrays downstream or select the top-level `currentEmployer` field.\n\n## API usage with cURL\n\nStart a run and wait for completion:\n\n```bash\ncurl -X POST \\\n  \"https://api.apify.com/v2/acts/automation-lab~linkedin-public-people-profile-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"startUrls\": [\n      {\"url\": \"https://www.linkedin.com/in/williamhgates/\"}\n    ],\n    \"maxItems\": 1\n  }'\n```\n\nKeep your Apify token in a secret manager. Do not place it in source code or a public task.\n\n## API usage with JavaScript\n\n```javascript\nimport { ApifyClient } from 'apify-client';\n\nconst client = new ApifyClient({ token: process.env.APIFY_TOKEN });\nconst run = await client.actor(\n  'automation-lab/linkedin-public-people-profile-scraper'\n).call({\n  startUrls: [\n    { url: 'https://www.linkedin.com/in/satyanadella/' },\n    { url: 'https://www.linkedin.com/in/jeffweiner08/' },\n  ],\n  maxItems: 2,\n});\n\nconst { items } = await client.dataset(run.defaultDatasetId).listItems();\nfor (const profile of items) {\n  console.log(profile.fullName, profile.currentEmployer, profile.linkedinUrl);\n}\n```\n\n## API usage with Python\n\n```python\nimport os\nfrom apify_client import ApifyClient\n\nclient = ApifyClient(os.environ[\"APIFY_TOKEN\"])\nrun = client.actor(\n    \"automation-lab/linkedin-public-people-profile-scraper\"\n).call(run_input={\n    \"startUrls\": [\n        {\"url\": \"https://www.linkedin.com/in/williamhgates/\"}\n    ],\n    \"maxItems\": 1,\n})\n\nfor profile in client.dataset(run[\"defaultDatasetId\"]).iterate_items():\n    print(profile[\"fullName\"], profile.get(\"headline\"))\n```\n\n## Use with Apify MCP\n\nAdd the Actor to Claude Code:\n\n```bash\nclaude mcp add --transport http apify \\\n  \"https://mcp.apify.com?tools=automation-lab/linkedin-public-people-profile-scraper\"\n```\n\n**Claude Desktop setup:** add this server object to `claude_desktop_config.json`.\n\n**Cursor setup:** add the same server object under MCP settings.\n\n**VS Code setup:** add the same HTTP server URL to the editor's MCP server configuration.\n\n```json\n{\n  \"mcpServers\": {\n    \"apify\": {\n      \"url\": \"https://mcp.apify.com?tools=automation-lab/linkedin-public-people-profile-scraper\"\n    }\n  }\n}\n```\n\nExample prompts:\n\n- “Run the LinkedIn Public People Profile Scraper for these three public profile URLs and return name, location, employer, and warnings.”\n- “Enrich this supplied executive profile list and export the successful public rows as CSV.”\n- “Summarize the publicly exposed education fields for this defined LinkedIn profile cohort.”\n\n## Limits and expected failure behavior\n\n- Only public LinkedIn `/in/` URLs are supported.\n- LinkedIn decides which logged-out fields are exposed.\n- Masked asterisk values are discarded.\n- Private, deleted, invalid, or unavailable profiles produce no dataset row.\n- A run can succeed with fewer rows than supplied URLs when some profiles are unavailable.\n- A run fails when every supplied profile fails extraction.\n- The Actor does not log in, solve CAPTCHAs, bypass profile privacy, search LinkedIn, extract emails, download media files, or retrieve private connection data.\n- Public page structure and visibility can change without notice.\n\n## Troubleshooting\n\n### Why is experience or education incomplete?\n\nLinkedIn often shows only part of a profile to logged-out visitors and may replace values with asterisks. It can also mix company or board affiliations into `alumniOf`; those records are excluded unless they have an educational type or LinkedIn `/school/` URL. Check `warnings`. The Actor intentionally returns only qualifying fields exposed through the supported public surface.\n\n### Why did I receive fewer rows than URLs?\n\nReview the run log for skipped URLs. Common causes are a private or deleted profile, a malformed `/in/` URL, temporary LinkedIn blocking, or no public Person metadata. Retry temporary failures later, but do not expect the Actor to reveal private profiles.\n\n### Why did the entire run fail?\n\nThe Actor fails if no useful profile was extracted. Check that the input uses public `linkedin.com/in/...` URLs and test one known public profile before retrying a large batch.\n\n### Does an empty array mean the person has none?\n\nNo. An empty `skills`, `languages`, `experience`, or `education` array means no entries were exposed through the logged-out structured surface at extraction time.\n\n## Tips for reliable runs\n\n- Prefer canonical public profile URLs copied from LinkedIn.\n- Put multiple URLs into one run instead of starting one run per profile.\n- Keep `maxItems` close to the intended batch size.\n- Deduplicate upstream as well as relying on Actor deduplication.\n- Preserve `warnings` in downstream systems.\n- Use `scrapedAt` when comparing observations.\n- Do not use a temporary field omission as evidence that a person left a role or institution.\n\n## Legal and responsible use\n\nScrape only public data you are permitted to process. Follow applicable laws, contractual obligations, LinkedIn's terms, and Apify's policies. Establish a lawful purpose, minimize collected fields, secure exports, honor deletion or access rights where applicable, and avoid sensitive decisions based solely on scraped data.\n\nThis Actor is a technical data-extraction tool, not legal advice. You are responsible for your inputs, processing purpose, retention, and downstream use.\n\n## Related automation-lab Actors\n\n- [LinkedIn Profile URL Finder](https://apify.com/automation-lab/linkedin-profile-url-finder) discovers likely public profile URLs before enrichment.\n- [LinkedIn Public Profile Search Scraper](https://apify.com/automation-lab/linkedin-public-profile-search-scraper) returns public profile search result cards.\n- [LinkedIn Company Employees Scraper](https://apify.com/automation-lab/linkedin-company-employees-scraper) supports company-centered workforce discovery.\n- [LinkedIn Profile Contact Enricher](https://apify.com/automation-lab/linkedin-profile-contact-enricher) adds source-attributed public company contact context when you supply official domains.\n\nChoose this Actor when you already have public person profile URLs and need normalized public professional records.\n\n## FAQ\n\n### Does it require a LinkedIn account?\n\nNo. It uses logged-out public surfaces and Apify Proxy sessions.\n\n### Can I provide profile slugs instead of URLs?\n\nNo. Full public `/in/` URLs make scope explicit and reduce ambiguity.\n\n### Does it find emails or phone numbers?\n\nNo. It extracts professional profile fields exposed publicly by LinkedIn.\n\n### Does it search for people?\n\nNo. Supply known public profile URLs. Use a related URL finder or public profile search Actor for discovery.\n\n### Are all fields guaranteed?\n\nNo. `fullName` is required for a successful row; every other public field can be null or empty when LinkedIn does not expose it.\n\n### Can I schedule it?\n\nYes. Use an Apify Task and schedule, then send the dataset to your chosen destination.\n\n### Is each failed URL charged as a profile?\n\nNo. The `profile` event is emitted only after a useful profile has been parsed and before its row is saved.\n"
    },
    "buildNumber": "0.1.4",
    "actVersion": {
      "sourceType": "SOURCE_FILES",
      "buildTag": "latest",
      "versionNumber": "0.1"
    },
    "usage": {
      "ACTOR_COMPUTE_UNITS": 0.0020755555555555557
    }
  }
}