{
  "version": 1,
  "slug": "scrape-linkedin-companies-python-api",
  "title": "How to Scrape LinkedIn Company Data in Python Using an API",
  "excerpt": "Scrape LinkedIn company data from known page URLs in Python, retrieve useful firmographic fields, and qualify accounts for B2B sales research.",
  "cover": {
    "src": "/howto_images/scrape-linkedin-companies-python-api/scrape-linkedin-companies-python-api-cover.png",
    "optimized": "https://www.datablist.com/_next/image?url=%2Fhowto_images%2Fscrape-linkedin-companies-python-api%2Fscrape-linkedin-companies-python-api-cover.png&w=1200&q=75"
  },
  "url": "https://www.datablist.com/how-to/scrape-linkedin-companies-python-api",
  "contentMarkdown": "\nYou can scrape LinkedIn company data in Python by sending a list of company page\nURLs to Datablist's API with the Requests library. The API returns structured\ncompany records with fields such as industry, employee count, headquarters,\nwebsite, and description when they are available on the public company page.\n\nIn this guide, I use one readable Python function to retrieve records for n8n,\nPostHog, Back Market, and Datablist. Then I reuse those results to qualify\nsoftware accounts with 50 to 500 employees in the United States or Germany.\nThe qualified record keeps its website and description for deeper sales\nresearch.\n\n> 🔑 **Summary For Those In a Rush**\n>\n> Datablist turns reviewed LinkedIn company page URLs into structured company\n> records that you can use in a sales-research workflow. The Python example\n> below retrieves every result page, then applies a local account filter without\n> making a second API call.\n>\n> - **Input:** LinkedIn company page URLs.\n> - **Output:** Available company details and a local qualification\n>   decision, with the original URL and complete company output kept together.\n\n> 💡 **Prefer a no-code solution?** Use Datablist's [LinkedIn Company Page\n> Scraper](/enrichments/linkedin-company-page-scraper-bulk) to extract company\n> fields from known LinkedIn company page URLs without writing code.\n\n## Set up Python and your Datablist API key {#set-up-python-and-your-datablist-api-key}\nFor this Python example, I use the `requests` package. Datablist's REST API is\navailable on the Growth plan, and each company-page lookup consumes enrichment\ncredits.\n\nCreate a Personal API Key from your [Datablist API account\npage](https://app.datablist.com/account/api).\n\nCreate a file named `scrape_linkedin_companies.py`, then prepare a virtual\nenvironment and install Requests:\n\n```shell\npython -m venv .venv\nsource .venv/bin/activate\npython -m pip install requests\nexport DATABLIST_API_KEY=\"your-api-key\"\n```\n\nThe script reads the key from `DATABLIST_API_KEY` and sends it in the\n`X-API-Key` header.\n\n## Scrape LinkedIn company pages with one Python function {#scrape-linkedin-company-pages-with-one-python-function}\nThe source list below contains companies with different industries, sizes, and\nheadquarters countries. Each `account_id` becomes the API's `inputId`. Results\nare not joined by their position in the response, so this stable ID is what\nconnects each result to the correct source URL.\n\nPaste this complete script into `scrape_linkedin_companies.py`:\n\n```python\nimport json\nimport os\nimport time\nimport uuid\n\nimport requests\n\n\naccounts = [\n    {\n        \"account_id\": \"account-n8n\",\n        \"linkedin_company_url\": \"https://www.linkedin.com/company/n8n/\",\n    },\n    {\n        \"account_id\": \"account-posthog\",\n        \"linkedin_company_url\": \"https://www.linkedin.com/company/posthog/\",\n    },\n    {\n        \"account_id\": \"account-back-market\",\n        \"linkedin_company_url\": \"https://www.linkedin.com/company/back-market/\",\n    },\n    {\n        \"account_id\": \"account-datablist\",\n        \"linkedin_company_url\": \"https://www.linkedin.com/company/datablist/\",\n    },\n]\n\n\ndef scrape_companies(accounts, api_key):\n    \"\"\"Submit one task, wait for it to finish, and return every result.\"\"\"\n    tasks_url = \"https://api.datablist.com/v1/enrichments/tasks\"\n    headers = {\"X-API-Key\": api_key}\n    inputs = [\n        {\n            \"inputId\": account[\"account_id\"],\n            \"data\": {\"companyLinkedInUrl\": account[\"linkedin_company_url\"]},\n        }\n        for account in accounts\n    ]\n\n    unique_id = str(uuid.uuid4())\n    print(f\"Request ID: {unique_id}\")\n    payload = {\n        \"enrichmentId\": \"linkedin-company-enrichment\",\n        \"uniqueId\": unique_id,\n        \"settings\": {},\n        \"inputs\": inputs,\n    }\n\n    response = requests.post(tasks_url, headers=headers, json=payload)\n    response.raise_for_status()\n    task = response.json()\n    task_id = task[\"@id\"]\n    task_url = f\"{tasks_url}/{task_id}\"\n    print(f\"Task ID: {task_id}\")\n\n    while task[\"status\"] not in (\"completed\", \"failed\", \"stopped\"):\n        time.sleep(3)\n        response = requests.get(task_url, headers=headers)\n        response.raise_for_status()\n        task = response.json()\n\n    if task[\"status\"] != \"completed\":\n        raise RuntimeError(\n            f\"Task {task_id} {task['status']}: {task.get('error')}\"\n        )\n\n    results = []\n    results_url = f\"{task_url}/results\"\n    params = {\"limit\": 100}\n    while True:\n        response = requests.get(results_url, headers=headers, params=params)\n        response.raise_for_status()\n        page = response.json()\n        results.extend(page[\"results\"])\n        next_cursor = page[\"nextCursor\"]\n        if next_cursor is None:\n            break\n        params[\"cursor\"] = next_cursor\n\n    return results\n\n\napi_key = os.environ.get(\"DATABLIST_API_KEY\")\nif not api_key:\n    raise RuntimeError(\"Set DATABLIST_API_KEY before running this script.\")\n\ncompany_results = scrape_companies(accounts, api_key)\nprint(json.dumps(company_results, indent=2, ensure_ascii=False))\n```\n\nRun it with:\n\n```shell\npython scrape_linkedin_companies.py\n```\n\nThe main values to change are the records in `accounts`. Keep each\n`account_id` stable and unique, then replace `linkedin_company_url` with a\nreviewed LinkedIn company page. The function converts those records into the\nAPI's `inputs` format and sends the company URL as `companyLinkedInUrl`.\n`settings` stays empty because this enrichment has no public settings.\n\nThe POST creates a background enrichment task. The API responds with `202\nAccepted` and a task ID, while the Requests client waits by polling the status\nURL. This blocking Python code is convenient for a small script, but the task\nstill runs asynchronously on Datablist's side.\n\nThe first GET checks the task itself. A task can finish as `completed`, `failed`,\nor `stopped`. The script continues only after `completed`; otherwise, it raises\nan error that includes the task ID and the API's task error. Keep that ID when\nyou investigate because a failed or stopped task may still have results and\ncredit usage to review.\n\nOnce the task is complete, the final loop fetches result pages with a limit of\n100. It fetches the next page until `nextCursor` is `null`. Each result keeps\nthe `inputId` you supplied, which is why the later qualification loop can join\nresults correctly even when response order differs from input order.\n\n## Read the company result and understand the retrieval method {#read-the-company-result-and-understand-the-retrieval-method}\nThe LinkedIn URL identifies the company directly. Datablist normalizes that\norganization URL and retrieves the available public company-page data rather\nthan searching for and scoring several possible companies. For a named company\nURL, it starts from the public page and can fill unresolved fields from managed\nfallback sources while keeping useful values already found.\n\nThat distinction matters when you read the result. Optional fields depend on\nwhat is publicly available. A null headquarters address or a missing specialty\ndoes not mean that the whole company lookup failed.\n\nHere is the complete results page returned for the four URLs:\n\n```json\n{\n  \"taskId\": \"69516a973dc34481920f41ade748e917\",\n  \"results\": [\n    {\n      \"inputId\": \"account-n8n\",\n      \"status\": \"success\",\n      \"outputs\": {\n        \"companyName\": \"n8n\",\n        \"companyHQPostalCode\": \"10115\",\n        \"companyDescription\": \"n8n is a workflow automation platform that uniquely combines AI capabilities with business process automation. The platform enables connection to any app or API while maintaining the flexibility of code with the speed of no-code.\\n\\nReleased under a fair-code license, n8n can be self-hosted and is supported by a vibrant community of developers and builders. Users can start simple and layer complexity as needed - utilizing the visual builder for quick wins, and adding custom Javascript or Python code where more control is required, enabling the connection of anything to everything.\\n\\nn8n is privately held, with funding from Sequoia, Felicis Ventures, Firstminute Capital, Harpoon Ventures, and others. Individual investors include Eventbrite's Kevin Hartz and Supercell's IIkka Paananen. n8n was founded in 2019 and is headquartered in Berlin.\",\n        \"companyTagline\": \"Flexible AI workflow automation for technical teams.\",\n        \"companyIndustry\": \"Software Development\",\n        \"companySpecialities\": \"AI, SOAR, Workflow Automation, and AI agents\",\n        \"companyWebsite\": \"https://n8n.io\",\n        \"companyHQCity\": \"Berlin\",\n        \"linkedInCompanyId\": \"28491094\",\n        \"companyType\": \"Privately Held\",\n        \"companyHQCountry\": \"DE\",\n        \"companySize\": 1389,\n        \"companyLogoUrl\": \"https://media.licdn.com/dms/image/v2/D4D0BAQEgEphZqatXwA/company-logo_200_200/B4DaFY.Sa2JcAE-/0/1792494086070/n8n_logo?e=2147483647&v=beta&t=NTL4C6f5QAd_o9nXd15PO8pOMSSEoms_tJdDHEo6eLk\",\n        \"companyHQAddress\": \"Novalisstraße 10\",\n        \"companyFoundedYear\": 2019,\n        \"companyFollowerCount\": 442229\n      },\n      \"creditsUsed\": \"25\",\n      \"error\": null\n    },\n    {\n      \"inputId\": \"account-posthog\",\n      \"status\": \"success\",\n      \"outputs\": {\n        \"companyName\": \"PostHog\",\n        \"companyHQPostalCode\": null,\n        \"companyDescription\": \"At PostHog, we're working to increase the number of successful products in the world.\\n\\nUntil now, tools for building products have been fragmented. Product analytics, heatmaps, session recording, web analytics, feature flags, and A/B testing are all helpful, but no one wants to buy, send data to, and integrate multiple products.\\n\\nPostHog offers these tools (and more) in an integrated, open source platform which can be hosted in either the US or EU. Both versions are SOC2 certified, GDPR-ready, and HIPAA compliant. \\n\\nWe started PostHog during Y Combinator's W20 cohort and had the most successful B2B software launch on Hacker News since 2012 - with a product that was just 4 weeks old.\\n\\nWith over 100,000 users, we're default alive, growing 97% through word of mouth, and we are in the top 0.01% most popular repos on GitHub.\",\n        \"companyTagline\": \"Your product's context layer. Get started with npx @posthog/wizard\",\n        \"companyIndustry\": \"Software Development\",\n        \"companySpecialities\": \"analytics, open source, product analytics, product, data, engineering, self-driving product, session replay, context warehouse, AI, product context, and error tracking\",\n        \"companyWebsite\": \"https://posthog.com/\",\n        \"companyHQCity\": \"San Francisco\",\n        \"linkedInCompanyId\": \"37415928\",\n        \"companyType\": \"Privately Held\",\n        \"companyHQCountry\": \"US\",\n        \"companySize\": 234,\n        \"companyLogoUrl\": \"https://media.licdn.com/dms/image/v2/D560BAQGQUyE_CXUxIw/company-logo_200_200/B56aCMWEgaJAAE-/0/1789060991251/posthog_logo?e=2147483647&v=beta&t=WfTGRSqp5UfYL33sBXx3tZ1zmFUECfUPf7JtEGPAvbA\",\n        \"companyHQAddress\": null,\n        \"companyFoundedYear\": 2020,\n        \"companyFollowerCount\": 59696\n      },\n      \"creditsUsed\": \"25\",\n      \"error\": null\n    },\n    {\n      \"inputId\": \"account-back-market\",\n      \"status\": \"success\",\n      \"outputs\": {\n        \"companyName\": \"Back Market\",\n        \"companyHQPostalCode\": null,\n        \"companyDescription\": \"The world is waking up to what fast tech costs. Back Market is where the revolution shops. \\nWe make refurbished tech the smart, confident choice by building trust in quality, repair and circularity. New is not always better. Smarter is.\\n\\nAs a certified B Corp, our mission is to create a world that does more with what we already have by prolonging the lifespan of electronic devices through circularity and repair. We have grown by double digits year over year and now serve 18 million customers across 17 markets, avoiding more than 2 million tons of CO2 emissions*\\n*Back Market calculation based on ADEME 2022 methodology.\\n\\nFounded in Paris in 2014 by Thibaud Hug de Larauze, Quentin Le Brouster and Vianney Vaute, Back Market is a leading global marketplace where people can buy professionally refurbished technology or trade in their devices.\\n\\nAre you ready to join us?\",\n        \"companyTagline\": \"Where the world shops refurbished tech.\",\n        \"companyIndustry\": \"Internet Marketplace Platforms\",\n        \"companySpecialities\": \"refurbished tech, marketplace, sustainability, circularity, Right to Repair, sustainable solutions, refurbished marketplace, circular economy, and sustainable alternatives\",\n        \"companyWebsite\": \"http://www.backmarket.com\",\n        \"companyHQCity\": \"Paris\",\n        \"linkedInCompanyId\": \"4793443\",\n        \"companyType\": \"Privately Held\",\n        \"companyHQCountry\": \"FR\",\n        \"companySize\": 1054,\n        \"companyLogoUrl\": \"https://media.licdn.com/dms/image/v2/D4E0BAQHRv6UClt5D2g/company-logo_200_200/B4EZnoUG3tKgAI-/0/1760539209613/back_market_logo?e=2147483647&v=beta&t=Pywpeu1Qs3h0Cl8kgvYurmBfXbImm5QIIc-aQog1MqM\",\n        \"companyHQAddress\": null,\n        \"companyFoundedYear\": 2014,\n        \"companyFollowerCount\": 101878\n      },\n      \"creditsUsed\": \"25\",\n      \"error\": null\n    },\n    {\n      \"inputId\": \"account-datablist\",\n      \"status\": \"success\",\n      \"outputs\": {\n        \"companyName\": \"Datablist\",\n        \"companyHQPostalCode\": null,\n        \"companyDescription\": \"When people hear \\\"data,\\\" they think of something complicated. We will change this.\\n\\nDatablist is an AI-powered spreadsheet that helps you: get, manipulate, or clean data and create workflows that run automatically while you focus on what matters most.\\n\\nIf you want to separate Datablist.com into its main parts, you can say we have:\\n\\n- Deduplicating, grouping, and labeling data\\n- Cleaning and editing data\\n- Managing data and automated workflows\\n- 50+ B2B data sources and scrapers\\n- 3 AI agents:\\n    - Editing assistant\\n    - AI Research agent\\n    - Deduplication assistant\\n\\nThe top 3 reasons why companies choose Datablist.com are:\\n\\n1. Simplicity: you don't need to have technical knowledge or experience with formulas to get everything out of it.\\n2. Flexibility that allows you to do (almost) everything with your data and AI while keeping everything under your control.\\n3. Scalability that no other solution can match. Up to 2 million rows—forget Excel.\\n\\nGo on try it for free!\\n\\nhttps://www.Datablist.com\",\n        \"companyTagline\": \"Let your tasks become workflows: automated workflows.\",\n        \"companyIndustry\": \"Information Technology & Services\",\n        \"companyWebsite\": \"https://www.datablist.com\",\n        \"companyHQCity\": \"Paris\",\n        \"linkedInCompanyId\": \"69842428\",\n        \"companyType\": \"Privately Held\",\n        \"companyHQCountry\": \"FR\",\n        \"companySize\": 3,\n        \"companyLogoUrl\": \"https://media.licdn.com/dms/image/v2/C4E0BAQFNFlMb8KEPRQ/company-logo_200_200/company-logo_200_200/0/1630622803412?e=2147483647&v=beta&t=UVkk7BOerChiWgS9Y06zOpT08SlK4kpAWGDYiVvUEVU\",\n        \"companyFollowerCount\": 134,\n        \"companyFoundedYear\": 2020,\n        \"companyHQAddress\": null\n      },\n      \"creditsUsed\": \"25\",\n      \"error\": null\n    }\n  ],\n  \"nextCursor\": null\n}\n```\n\nAll four rows succeeded and used 25 credits each. PostHog returned `Software\nDevelopment`, 234 employees, US headquarters, its website, and its description.\nn8n is also a software company in a selected country, but its returned employee\ncount is 1,389. Back Market returned `Internet Marketplace Platforms`, 1,054\nemployees, and FR headquarters. Datablist returned `Information Technology &\nServices`, 3 employees, and FR headquarters.\n\nThe complete object is useful because account research rarely stops at one\nfield. You can qualify with industry, employee count, and country while keeping\nthe exact returned website and description for the next research step.\n\nIn this response, `companySize` is the returned employee count, not a LinkedIn\nsize-range label. `companyHQCountry` is the headquarters location, not every\ncountry where the company operates. Those distinctions matter when you adapt\nthe size and country filters to your ideal customer profile.\n\nOptional fields do not all have to be present for qualification. A missing\naddress or specialty can remain empty when industry, employee count, and\nheadquarters country are available. Keep `companyWebsite` as returned instead\nof assuming the API converts it to a bare domain. The description can help a\nsales researcher understand the company's product and prepare account-specific\nresearch before outreach. The result-level `status`, `creditsUsed`, and `error`\nshould stay beside the company fields when you review or store the record.\n\n## Qualify accounts for sales research {#qualify-accounts-for-sales-research}\nSuppose your target is a software or technology company with 50 to 500 employees\nand headquarters in the US or Germany. Add this loop below the complete script.\nIt reuses `company_results`, so it does not submit another task:\n\n```python\naccounts_by_id = {account[\"account_id\"]: account for account in accounts}\nqualified_accounts = []\nother_accounts = []\n\nfor result in company_results:\n    account = accounts_by_id.get(result[\"inputId\"])\n    qualification = \"needs_review\"\n\n    if result[\"status\"] == \"success\":\n        outputs = result[\"outputs\"]\n        industry = outputs.get(\"companyIndustry\")\n        company_size = outputs.get(\"companySize\")\n        country = outputs.get(\"companyHQCountry\")\n\n        if industry and isinstance(company_size, (int, float)) and country:\n            industry_matches = any(\n                keyword in industry.lower()\n                for keyword in (\"software\", \"technology\")\n            )\n            size_matches = 50 <= company_size <= 500\n            country_matches = country in {\"US\", \"DE\"}\n            if industry_matches and size_matches and country_matches:\n                qualification = \"qualified\"\n            else:\n                qualification = \"not_qualified\"\n\n    source_url = None\n    if account is not None:\n        source_url = account[\"linkedin_company_url\"]\n\n    reviewed_account = dict(result)\n    reviewed_account[\"sourceLinkedInUrl\"] = source_url\n    reviewed_account[\"qualification\"] = qualification\n\n    if qualification == \"qualified\":\n        qualified_accounts.append(reviewed_account)\n    else:\n        other_accounts.append(reviewed_account)\n\nprint(json.dumps(qualified_accounts, indent=2, ensure_ascii=False))\n```\n\nPostHog maps back to `account-posthog` even if the API returns rows in a\ndifferent order. It qualifies because `Software Development`, 234 employees,\nand `US` pass all three checks. The loop copies the original result with its\ncomplete `outputs`, returned `https://posthog.com/` website, description,\n`creditsUsed`, and `error`. It adds only the source URL and local qualification\nlabel.\n\nn8n maps to `account-n8n` and does not qualify because 1,389 employees exceeds\nthe upper limit. Back Market misses the selected industry, size, and country,\nwhile Datablist is below the size range and outside the selected countries.\nFuture unsuccessful or incomplete rows receive `needs_review` rather than being\nsilently discarded.\n\n![LinkedIn company page results are filtered by industry, employee count, and headquarters country for account qualification](/howto_images/scrape-linkedin-companies-python-api/illustrations/linkedin-company-account-qualification.png)\n\nYou can download the maintained [synchronous Requests\nexample](https://github.com/datablist/linkedin-company-scraper-api/blob/main/python/sync_requests.py)\nor use the repository's [async HTTPX\nalternative](https://github.com/datablist/linkedin-company-scraper-api/blob/main/python/async_httpx.py)\nwhen you need non-blocking application code.\n\n## Estimate LinkedIn company scraping credits {#estimate-linkedin-company-scraping-credits}\nA standard named LinkedIn company page uses 25 credits. A numerical company-ID\nURL can use 75 credits when the final fallback is needed, but 75 credits is not\nthe price of every numerical URL. Failed or no-match lookups can also consume\ncredits, so use the task's total and each result's `creditsUsed` value as the\nrecord of actual usage.\n\nThe four named URLs in this guide used 100 credits in total. At the standard\nrate, 1,000 company pages use 25,000 credits. Numerical-ID-heavy input needs\nmore room for the possible 75-credit path.\n\nREST API access requires the Growth plan. The monthly plan includes 20,000\ncredits, and top-up credits are a separate purchase. At the current USD base\ntop-up of $20 for 20,000 credits, one credit costs $0.001. At that rate, 25,000\ncredits are worth $25. Check [Datablist pricing](/pricing) for the current plan\nand credit catalog.\n\n<div format=\"cost-calculator\"\n     data-heading=\"Estimate your API cost\"\n     data-unit-label=\"Company pages\"\n     data-credits-per-unit=\"25\"\n     data-max-credits-per-unit=\"75\"\n     data-lower-rate-label=\"Standard company page\"\n     data-upper-rate-label=\"Numerical-ID fallback\"\n     data-initial-quantity=\"1000\"\n     data-max-quantity=\"100000\">\n<p>Standard company pages use 25 credits; a numerical-ID lookup can use 75 credits when the final fallback is needed.</p>\n</div>\n\n## Fix common input, task, and result problems {#fix-common-input-task-and-result-problems}\n- **`DATABLIST_API_KEY` is missing:** the script stops before sending a request.\n  Export the variable and run the file again.\n- **The API rejects the key:** verify your Personal API Key and keep it in the\n  `X-API-Key` header used by the example.\n- **The source URL is invalid:** replace a personal profile, unrelated site,\n  blank value, or malformed URL with a supported public LinkedIn company,\n  school, showcase, numerical-ID, or Sales Navigator company URL.\n- **Credits are insufficient:** check your balance, then use included credits or\n  add a top-up before creating another task.\n- **The task is `failed` or `stopped`:** inspect its error, existing results,\n  and credit usage before deciding whether to submit another paid task.\n- **A result is sparse, `no_result`, or `error`:** keep its source URL, status,\n  credits, and error for review. Confirm that the input is a public company page\n  before resubmitting, without guessing why the data was unavailable.\n\nIf the creation response is lost, use the request ID that the script printed\nbefore the POST. List tasks first and match that value in `uniqueId`:\n\n```shell\ncurl \"https://api.datablist.com/v1/enrichments/tasks?limit=100\" \\\n  -H \"X-API-Key: $DATABLIST_API_KEY\"\n```\n\nThe matching task's `@id` is the task ID. Use it to continue checking\n`/v1/enrichments/tasks/{taskId}` or to fetch\n`/v1/enrichments/tasks/{taskId}/results` instead of automatically creating\nanother task. A single request can contain up to 10,000 inputs and 10 MiB; see\nthe [task documentation](https://platform.datablist.com/docs/enrichments/tasks)\nfor retention and other lifecycle details.\n\n## Next steps {#next-steps}\nYou now have one reusable Python Requests function that turns a list of LinkedIn\ncompany page URLs into structured company records, plus a local qualification\nloop that preserves the full API output for sales research.\n\nReplace the four examples with a small reviewed account list, keep stable IDs,\nand adjust the industry, employee-count, and country rules to match your ideal\ncustomer profile. If your starting points are people rather than companies, use\nthe [LinkedIn profile scraper API guide](/how-to/scrape-linkedin-profiles-python-api).\nIf you only have company names, start by [finding company websites in\nPython](/how-to/find-company-websites-python-api).\n"
}