LinkedIn intelligence for B2B go-to-market
Turn LinkedIn's three data pillars — companies (firmographics), people (profiles), and jobs (postings) — into four sales/RevOps workflows: TAM sizing, prospect list building, headcount benchmarking, and hiring-signal selling.
The key insight
LinkedIn value comes from chaining the three pillars, not from any one scraper:
- A company search gives you the accounts in a market (TAM).
- A people search inside those accounts gives you the contacts (prospects).
- A jobs feed for those accounts gives you the timing (hiring signals = budget + pain).
So the routing decision is "which pillar(s) does this question touch", then chain them. The output of one pillar (a company slug, a company name, a profile URL) is the input to the next.
| Pillar |
Answers |
Primary use |
| Company |
"which/how many companies, how big" |
TAM sizing, headcount benchmarking |
| People |
"who are the decision-makers" |
prospect/lead lists, ICP enrichment |
| Jobs |
"who is hiring, for what, when" |
hiring-signal selling, expansion timing |
Always confirm the input schema before running (apify actors info "ACTOR_ID" --input --json). LinkedIn Actor inputs differ between Actors and change often — the same intent ("scrape a company") uses profileUrls in one Actor and companyUrls/identifier in another. A wrong field name silently returns 0 results.
Prerequisites
- Apify account (sign up)
- Authentication via one of:
Workflow
- Classify the goal into one of the four workflows below (or a combination), and identify which pillar(s) it needs.
- Resolve identifiers first. LinkedIn Actors key off exact slugs/URLs, not loose names. Discover them via SERP before scraping —
apify/google-search-scraper with "<company> site:linkedin.com/company" (company slug) or "<title> <company> site:linkedin.com/in" (people). A wrong slug returns 0 rows silently.
- Confirm the schema. Run
apify actors info "ACTOR_ID" --input --json and build input against the actual field names. Note where output lands (dataset vs key-value store — see gotchas).
- Estimate cost, then run. LinkedIn Actors are usually
PAY_PER_EVENT (per profile/company/job). Multiply by your row count and confirm with the user if the estimate is significant (see references/gotchas.md).
- Deliver. Report row count, the columns, and a link to the dataset/console. Default to CSV for lists; summarize counts/medians for TAM and benchmarking.
The four workflows
| Workflow |
Pillars chained |
Recipe |
| TAM sizing |
Company |
Search companies by industry + headcount band + geo → dedupe → report count, size distribution, and the account list. Treat counts as modeled/directional, not exact. |
| Prospect list building |
Company → People |
Get target accounts (or take a user list) → people-search each by title/seniority → enrich profiles → output one contact per row, deduped. |
| Headcount benchmarking |
Company |
Scrape firmographics for a competitor set → compare employeeCount, follower count, industry, HQ → rank. Re-run on a schedule to track headcount growth over time. |
| Hiring-signal selling |
Jobs (→ Company → People) |
Pull recent job postings for target accounts/roles → a new posting = active budget + pain → optionally chain to people-search for the hiring manager → draft timed outreach. |
Actor routing
LinkedIn is a protected platform — never use a generic crawler (website-content-crawler, rag-web-browser) on linkedin.com. Use a dedicated Actor.
| User need |
Actor ID |
Tier |
Best for |
| Company firmographics |
dev_fusion/Linkedin-Company-Scraper |
community |
Employee count, industry, HQ, followers, about. Output lands in the key-value store, not the dataset. Field is profileUrls. |
| Company firmographics (alt) |
harvestapi/linkedin-company |
community |
Firmographics with dataset output; good when you have many slugs. |
| People profiles (enrich URLs) |
harvestapi/linkedin-profile-scraper |
community |
Full profile from a profile URL: title, company, location, experience, headline. |
| People search (build lists) |
harvestapi/linkedin-profile-search |
community |
Find people by keywords/title/company/location without knowing URLs — the prospecting engine. |
| Job postings |
curious_coder/linkedin-jobs-scraper |
community |
Jobs from a LinkedIn jobs search URL (not keywords). count min is 10. Returns company firmographics too (companyEmployeesCount, companyWebsite). |
| Job postings (alt) |
harvestapi/linkedin-job |
community |
Jobs feed with dataset output; pair with company search. |
| Resolve slugs / profile URLs |
apify/google-search-scraper |
apify |
Discover the exact linkedin.com/company/<slug> or linkedin.com/in/<handle> before scraping. |
Tier = apify (Apify-maintained, prefer) or community (third-party). These are starting points — run apify actors search "linkedin" --json --limit 20 to find current alternatives, and always confirm the schema (step 3). See references/actor-index.md for the full table and field notes.
Calling Actors — Apify CLI
Three flags on every call (--json, --user-agent, 2>/dev/null):
# 0. Resolve the exact company slug first (names != slugs: "Oxylabs" -> "oxylabs-io")
apify actors call "apify/google-search-scraper" \
-i '{"queries":"Acme Corp site:linkedin.com/company","maxPagesPerQuery":1,"resultsPerPage":5}' \
--json \
--user-agent apify-awesome-skills/apify-linkedin-intelligence \
2>/dev/null
# 1. Company firmographics (TAM / headcount benchmarking) — output is in the KV store
apify actors call "dev_fusion/Linkedin-Company-Scraper" \
-i '{"profileUrls":["https://www.linkedin.com/company/oxylabs-io/"]}' \
--json \
--user-agent apify-awesome-skills/apify-linkedin-intelligence \
2>/dev/null
# 2. People search (build a prospect list by title + company + geo)
apify actors call "harvestapi/linkedin-profile-search" \
-i '{"currentCompanies":["Acme Corp"],"jobTitles":["VP Sales","Head of Revenue"],"locations":["United States"],"maxItems":50}' \
--json \
--user-agent apify-awesome-skills/apify-linkedin-intelligence \
2>/dev/null
# 3. Enrich specific profile URLs
apify actors call "harvestapi/linkedin-profile-scraper" \
-i '{"profileUrls":["https://www.linkedin.com/in/some-handle/"]}' \
--json \
--user-agent apify-awesome-skills/apify-linkedin-intelligence \
2>/dev/null
# 4. Hiring signals — jobs from a LinkedIn jobs SEARCH URL (not keywords), count >= 10
apify actors call "curious_coder/linkedin-jobs-scraper" \
-i '{"urls":["https://www.linkedin.com/jobs/search/?keywords=Account%20Executive&location=United%20States"],"count":10,"scrapeCompany":true}' \
--json \
--user-agent apify-awesome-skills/apify-linkedin-intelligence \
2>/dev/null
# Inspect input schema (DO THIS before every new Actor) / fetch dataset results
apify actors info "harvestapi/linkedin-profile-search" --input --json \
--user-agent apify-awesome-skills/apify-linkedin-intelligence 2>/dev/null
apify datasets get-items DATASET_ID --format json \
--user-agent apify-awesome-skills/apify-linkedin-intelligence 2>/dev/null
The input fields above are illustrative — confirm them against each Actor's real schema before running. The Apify MCP server (https://mcp.apify.com) and any MCP client (e.g. mcpc) are equivalent alternatives.
Troubleshooting
- 0 rows returned → almost always a wrong slug/name/URL or wrong input field. Re-resolve the slug via SERP (step 2), and re-check the field name against
apify actors info ... --input.
- Company scraper "returned nothing" →
dev_fusion/Linkedin-Company-Scraper writes to the key-value store, not the dataset. Read the KV store keys after the run.
- Jobs Actor rejects keywords →
curious_coder/linkedin-jobs-scraper needs a full linkedin.com/jobs/search/?... URL, and count min is 10. URL-encode multi-word values (Bright Data → Bright%20Data).
- Headcount looks off → LinkedIn employee/TAM counts are modeled and directional, not a ground-truth census. Label them as estimates; cross-check against the company website or jobs Actor's
companyEmployeesCount.
- Cost climbing on a big list → these Actors bill per result. Cap
maxItems, run a small sample first to validate the schema, then scale. See references/gotchas.md.
1---2name: apify-linkedin-intelligence3description: Turn LinkedIn into a B2B go-to-market data source using Apify Actors across the three LinkedIn data pillars — company firmographics, people profiles, and job postings. Use for TAM sizing (count and size companies in a market), prospect / lead list building (find decision-makers by title, company, seniority, geo), headcount benchmarking (compare employee counts and growth across competitors), and hiring-signal selling (turn new job postings into buying signals and timed outreach). Trigger phrases include "size my TAM on LinkedIn", "build a prospect list of <title> at <companies>", "find decision makers / ICP contacts", "how many employees does <company> have", "benchmark headcount vs competitors", "who is hiring for <role>", "hiring signals for sales", "LinkedIn company / people / jobs scraper", or "enrich these LinkedIn URLs". Chains firmographics + people + jobs instead of a single scraper, and confirms input schemas before running because LinkedIn Actor inputs drift.4---56# LinkedIn intelligence for B2B go-to-market78Turn LinkedIn's three data pillars — **companies** (firmographics), **people** (profiles), and **jobs** (postings) — into four sales/RevOps workflows: TAM sizing, prospect list building, headcount benchmarking, and hiring-signal selling.910## The key insight1112LinkedIn value comes from **chaining the three pillars**, not from any one scraper:1314- A **company search** gives you the *accounts* in a market (TAM).15- A **people search** inside those accounts gives you the *contacts* (prospects).16- A **jobs feed** for those accounts gives you the *timing* (hiring signals = budget + pain).1718So the routing decision is "which pillar(s) does this question touch", then chain them. The output of one pillar (a company slug, a company name, a profile URL) is the input to the next.1920| Pillar | Answers | Primary use |21|--------|---------|-------------|22| Company | "which/how many companies, how big" | TAM sizing, headcount benchmarking |23| People | "who are the decision-makers" | prospect/lead lists, ICP enrichment |24| Jobs | "who is hiring, for what, when" | hiring-signal selling, expansion timing |2526**Always confirm the input schema before running** (`apify actors info "ACTOR_ID" --input --json`). LinkedIn Actor inputs differ between Actors and change often — the same intent ("scrape a company") uses `profileUrls` in one Actor and `companyUrls`/`identifier` in another. A wrong field name silently returns 0 results.2728## Prerequisites2930- Apify account ([sign up](https://apify.com))31- Authentication via one of:32 - `apify login` (OAuth, if using the Apify CLI)33 - `APIFY_TOKEN` environment variable34 - Token from [Apify Console → Settings → Integrations](https://console.apify.com/settings/integrations)3536## Workflow37381. **Classify the goal** into one of the four workflows below (or a combination), and identify which pillar(s) it needs.392. **Resolve identifiers first.** LinkedIn Actors key off exact slugs/URLs, not loose names. Discover them via SERP before scraping — `apify/google-search-scraper` with `"<company> site:linkedin.com/company"` (company slug) or `"<title> <company> site:linkedin.com/in"` (people). A wrong slug returns 0 rows silently.403. **Confirm the schema.** Run `apify actors info "ACTOR_ID" --input --json` and build input against the *actual* field names. Note where output lands (dataset vs key-value store — see gotchas).414. **Estimate cost, then run.** LinkedIn Actors are usually `PAY_PER_EVENT` (per profile/company/job). Multiply by your row count and confirm with the user if the estimate is significant (see [references/gotchas.md](references/gotchas.md)).425. **Deliver.** Report row count, the columns, and a link to the dataset/console. Default to CSV for lists; summarize counts/medians for TAM and benchmarking.4344### The four workflows4546| Workflow | Pillars chained | Recipe |47|----------|-----------------|--------|48| **TAM sizing** | Company | Search companies by industry + headcount band + geo → dedupe → report count, size distribution, and the account list. Treat counts as **modeled/directional**, not exact. |49| **Prospect list building** | Company → People | Get target accounts (or take a user list) → people-search each by title/seniority → enrich profiles → output one contact per row, deduped. |50| **Headcount benchmarking** | Company | Scrape firmographics for a competitor set → compare `employeeCount`, follower count, industry, HQ → rank. Re-run on a schedule to track headcount growth over time. |51| **Hiring-signal selling** | Jobs (→ Company → People) | Pull recent job postings for target accounts/roles → a new posting = active budget + pain → optionally chain to people-search for the hiring manager → draft timed outreach. |5253## Actor routing5455LinkedIn is a protected platform — **never** use a generic crawler (`website-content-crawler`, `rag-web-browser`) on `linkedin.com`. Use a dedicated Actor.5657| User need | Actor ID | Tier | Best for |58|-----------|----------|------|----------|59| Company firmographics | `dev_fusion/Linkedin-Company-Scraper` | community | Employee count, industry, HQ, followers, about. **Output lands in the key-value store, not the dataset.** Field is `profileUrls`. |60| Company firmographics (alt) | `harvestapi/linkedin-company` | community | Firmographics with dataset output; good when you have many slugs. |61| People profiles (enrich URLs) | `harvestapi/linkedin-profile-scraper` | community | Full profile from a profile URL: title, company, location, experience, headline. |62| People search (build lists) | `harvestapi/linkedin-profile-search` | community | Find people by keywords/title/company/location without knowing URLs — the prospecting engine. |63| Job postings | `curious_coder/linkedin-jobs-scraper` | community | Jobs from a LinkedIn jobs **search URL** (not keywords). `count` min is 10. Returns company firmographics too (`companyEmployeesCount`, `companyWebsite`). |64| Job postings (alt) | `harvestapi/linkedin-job` | community | Jobs feed with dataset output; pair with company search. |65| Resolve slugs / profile URLs | `apify/google-search-scraper` | apify | Discover the exact `linkedin.com/company/<slug>` or `linkedin.com/in/<handle>` before scraping. |6667`Tier` = `apify` (Apify-maintained, prefer) or `community` (third-party). These are starting points — run `apify actors search "linkedin" --json --limit 20` to find current alternatives, and always confirm the schema (step 3). See [references/actor-index.md](references/actor-index.md) for the full table and field notes.6869## Calling Actors — Apify CLI7071Three flags on every call (`--json`, `--user-agent`, `2>/dev/null`):7273 # 0. Resolve the exact company slug first (names != slugs: "Oxylabs" -> "oxylabs-io")74 apify actors call "apify/google-search-scraper" \75 -i '{"queries":"Acme Corp site:linkedin.com/company","maxPagesPerQuery":1,"resultsPerPage":5}' \76 --json \77 --user-agent apify-awesome-skills/apify-linkedin-intelligence \78 2>/dev/null7980 # 1. Company firmographics (TAM / headcount benchmarking) — output is in the KV store81 apify actors call "dev_fusion/Linkedin-Company-Scraper" \82 -i '{"profileUrls":["https://www.linkedin.com/company/oxylabs-io/"]}' \83 --json \84 --user-agent apify-awesome-skills/apify-linkedin-intelligence \85 2>/dev/null8687 # 2. People search (build a prospect list by title + company + geo)88 apify actors call "harvestapi/linkedin-profile-search" \89 -i '{"currentCompanies":["Acme Corp"],"jobTitles":["VP Sales","Head of Revenue"],"locations":["United States"],"maxItems":50}' \90 --json \91 --user-agent apify-awesome-skills/apify-linkedin-intelligence \92 2>/dev/null9394 # 3. Enrich specific profile URLs95 apify actors call "harvestapi/linkedin-profile-scraper" \96 -i '{"profileUrls":["https://www.linkedin.com/in/some-handle/"]}' \97 --json \98 --user-agent apify-awesome-skills/apify-linkedin-intelligence \99 2>/dev/null100101 # 4. Hiring signals — jobs from a LinkedIn jobs SEARCH URL (not keywords), count >= 10102 apify actors call "curious_coder/linkedin-jobs-scraper" \103 -i '{"urls":["https://www.linkedin.com/jobs/search/?keywords=Account%20Executive&location=United%20States"],"count":10,"scrapeCompany":true}' \104 --json \105 --user-agent apify-awesome-skills/apify-linkedin-intelligence \106 2>/dev/null107108 # Inspect input schema (DO THIS before every new Actor) / fetch dataset results109 apify actors info "harvestapi/linkedin-profile-search" --input --json \110 --user-agent apify-awesome-skills/apify-linkedin-intelligence 2>/dev/null111 apify datasets get-items DATASET_ID --format json \112 --user-agent apify-awesome-skills/apify-linkedin-intelligence 2>/dev/null113114The input fields above are **illustrative** — confirm them against each Actor's real schema before running. The Apify MCP server (<https://mcp.apify.com>) and any MCP client (e.g. `mcpc`) are equivalent alternatives.115116## Troubleshooting117118- **0 rows returned** → almost always a wrong slug/name/URL or wrong input field. Re-resolve the slug via SERP (step 2), and re-check the field name against `apify actors info ... --input`.119- **Company scraper "returned nothing"** → `dev_fusion/Linkedin-Company-Scraper` writes to the **key-value store**, not the dataset. Read the KV store keys after the run.120- **Jobs Actor rejects keywords** → `curious_coder/linkedin-jobs-scraper` needs a full `linkedin.com/jobs/search/?...` URL, and `count` min is 10. URL-encode multi-word values (`Bright Data` → `Bright%20Data`).121- **Headcount looks off** → LinkedIn employee/TAM counts are **modeled and directional**, not a ground-truth census. Label them as estimates; cross-check against the company website or jobs Actor's `companyEmployeesCount`.122- **Cost climbing on a big list** → these Actors bill per result. Cap `maxItems`, run a small sample first to validate the schema, then scale. See [references/gotchas.md](references/gotchas.md).