---
name: linkedin-company
description: "Get public LinkedIn company data from a company URL or website domain: industry, size, employees on LinkedIn, headquarters and address, all locations, website, type, founding year, specialties, followers, similar companies and recent posts. No login. Use when the user wants firmographics for companies from their public LinkedIn page: industry, size range, employees on LinkedIn, headquarters and address, office locations, website, company type, founding year, specialties, followers, similar companies or recent posts. Accepts LinkedIn company URLs or plain company websites/domains, for example to qualify or enrich a lead list. Calls the Pullwire API with the key in PULLWIRE_API_KEY; costs 1 credit per company profile."
metadata:
  openclaw:
    requires:
      env:
        - PULLWIRE_API_KEY
      bins:
        - curl
    primaryEnv: PULLWIRE_API_KEY
    homepage: https://pullwire.dev/tools/linkedin-company
---

# LinkedIn Company Scraper

Get public LinkedIn company data from a company URL or website domain: industry, size, employees on LinkedIn, headquarters and address, all locations, website, type, founding year, specialties, followers, similar companies and recent posts. No login. The data comes from the Pullwire API: you send the input as JSON and get structured JSON back. Only public data.

## Setup (once)

The API key must be in the environment variable `PULLWIRE_API_KEY`. Keys start with `af_live_`. Check it without printing it:

```bash
test -n "$PULLWIRE_API_KEY" && echo "key is set" || echo "PULLWIRE_API_KEY is missing"
```

If it is missing, stop and tell the user: sign up at https://pullwire.dev/login (new accounts get 2,000 free credits), create a key at https://pullwire.dev/app/keys, then run `export PULLWIRE_API_KEY="af_live_..."` in the shell the agent uses. Never print the key, write it into a file or repeat it in the chat.

## Run it

```bash
curl -sS -X POST "https://pullwire.dev/api/v1/tools/linkedin-company/runs" \
  -H "x-api-key: $PULLWIRE_API_KEY" \
  -H "content-type: application/json" \
  -d '{"companies":["https://www.linkedin.com/company/canva","hubspot.com"],"maxResults":3}'
```

The request waits up to 25 seconds and answers with `{"run": {...}, "results": [...]}`. Check `run.status` (`succeeded` or `failed`) and `run.error`: when it is set, the run stopped early and `results` is partial (everything in it was charged). `run.creditsUsed` is what the run cost. For big answers add `-o result.json` and read only the fields you need.

Good to know:

- Put every company into one run (`companies`, up to 1,000) instead of one run per company. A LinkedIn URL and the same company’s domain are scraped once.
- A domain is matched through the LinkedIn link on its homepage, else its name as LinkedIn name, kept only when LinkedIn lists the same website. `matchedBy` says which; `websiteMatchesInput: false` means the company’s own site links that page but LinkedIn lists another domain: mention it.
- `companySize` is LinkedIn’s range (`employeeCountMin`/`employeeCountMax` as numbers); `employeesOnLinkedin` counts members who list the company, which can be above or below the range.
- LinkedIn shows pages by numeric id (`/company/1441`) and school pages to signed-in members only: they fail with `UNAVAILABLE` and cost nothing. A website without a findable LinkedIn page fails with `NOT_FOUND` and costs nothing.
- Set `includePosts` to `false` when posts are not needed: the answer stays much smaller.
- This site blocks cloud servers, so runs go through residential network access and are switched on only while our automatic checks pass. When a run answers `503 TOOL_UNAVAILABLE`, tell the user this source is paused for now (nothing was charged) and check https://pullwire.dev/status.

## Input

The JSON body is the tool input. Fields:

| Field | Type | Required | Default | What it does |
| --- | --- | --- | --- | --- |
| `companies` | string[] | yes |  | Companies: LinkedIn company or showcase page URLs (linkedin.com/company/<name>) or company websites/domains (e.g. "hubspot.com"), one per line. A domain is matched to its LinkedIn page through the link on its homepage. One result per company; duplicates are skipped. |
| `includePosts` | boolean | no | `true` | Include recent posts: Add the latest posts shown on the public page (up to about 10): text, date, likes and link. |
| `maxResults` | integer | no |  | Max results: Stop after this many companies. Empty: all of them. |

Without `maxResults` a run returns at most 20 results; the maximum is 200 per run. Credits for `maxResults` results are reserved before the run starts and the unused part is given back, so ask for what you need.

## Output

Each item in `results` has these fields:

| Field | Type | Always there |
| --- | --- | --- |
| `input` | string | yes |
| `matchedBy` | "linkedin-url" \| "website-link" \| "name-guess" | yes |
| `linkedinUrl` | string | yes |
| `pageType` | "company" \| "showcase" | yes |
| `slug` | string | yes |
| `companyId` | string | no |
| `name` | string | yes |
| `tagline` | string | no |
| `description` | string | no |
| `website` | string | no |
| `websiteDomain` | string | no |
| `websiteMatchesInput` | boolean | no |
| `industry` | string | no |
| `companySize` | string | no |
| `employeeCountMin` | integer | no |
| `employeeCountMax` | integer | no |
| `employeesOnLinkedin` | integer | no |
| `headquarters` | string | no |
| `address` | object | no |
| `companyType` | string | no |
| `foundedYear` | integer | no |
| `specialties` | string[] | no |
| `followers` | integer | no |
| `logoUrl` | string | no |
| `locations` | object[] | yes |
| `affiliatedPages` | object[] | yes |
| `similarPages` | object[] | yes |
| `recentPosts` | object[] | no |
| `scrapedAt` | string | yes |

## Cost

1 credit per company profile. One LinkedIn company profile returned. Companies that cannot be found or loaded are not charged. Check the balance with:

```bash
curl -sS "https://pullwire.dev/api/v1/me" -H "x-api-key: $PULLWIRE_API_KEY"
```

The answer has `balance` in credits. Tell the user what a run cost when it was more than a few credits.

## Bigger jobs

Add `?async=1` to the run URL to start a background run (up to 50 seconds). It answers `202` with `run.id` right away. Poll every few seconds until `status` is `succeeded` or `failed`, then fetch the results:

```bash
curl -sS "https://pullwire.dev/api/v1/runs/$RUN_ID" -H "x-api-key: $PULLWIRE_API_KEY"
curl -sS "https://pullwire.dev/api/v1/runs/$RUN_ID/results" -H "x-api-key: $PULLWIRE_API_KEY"
```

Add `?format=csv` to the results URL when the user wants a spreadsheet.

## Errors

Errors answer `{"error": {"code": "...", "message": "..."}}`.

| Status | Code | What to do |
| --- | --- | --- |
| 400 | `INVALID_INPUT` | The message names the field. Fix the input and retry; nothing was charged. |
| 401 | `UNAUTHORIZED` | The key is missing, wrong or revoked. Ask the user to set a valid key in `PULLWIRE_API_KEY`. |
| 402 | `INSUFFICIENT_CREDITS` | The balance cannot cover the reservation. Lower `maxResults` or tell the user the balance is too low. |
| 404 | `NOT_FOUND` | Unknown tool or run ID. |
| 429 | `RATE_LIMITED` | Used up its burst of 120 requests and its 60 per minute. Wait, then retry. |
| 500 | `INTERNAL_ERROR` | Retry later. If the body has `runId`, fetch the partial results with `GET /runs/{runId}/results`. |
| 503 | `TOOL_UNAVAILABLE` | Our latest automatic checks against this site failed, so its runs are paused. Nothing was reserved or charged. Tell the user and try again later. |
| 503 | `SERVICE_UNAVAILABLE` | Retry later. |

Docs: https://pullwire.dev/docs · Tool page: https://pullwire.dev/tools/linkedin-company
