# LinkedIn Public Jobs Scraper (no login) | Salary & Level (`transparent_meteorite/linkedin-public-jobs`) Actor

Search public guest LinkedIn job listings with no login or cookies: keywords, location, date, remote/hybrid, level, type, company. Title, company, salary, seniority, industries, full description. Deduped. Pay per job.

- **URL**: https://apify.com/transparent_meteorite/linkedin-public-jobs.md
- **Developed by:** [Detroit Web Agent - Open Data](https://apify.com/transparent_meteorite) (community)
- **Categories:** Jobs, Lead generation, Social media
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.85 / 1,000 job extracteds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## LinkedIn Public Jobs Scraper (no login): salary, seniority, industries

Search LinkedIn's public job listings the same way a logged-out visitor sees them. No account, no cookies, no profiles. Each row is one job with company, location, posted date (ISO), salary when the posting shows one, seniority, employment type, industries, applicant count text, company details and the full description.

- **In short:** LinkedIn Public Jobs Scraper (Apify actor `transparent_meteorite/linkedin-public-jobs`) reads only LinkedIn's public guest job pages (`jobs-guest` search and job-posting endpoints, plus the public company page) and returns clean, deduplicated job rows. No login, no cookies, no personal data.
- **Who it is for:** recruiters and staffing teams, job boards and aggregators, sales teams using hiring as a buying signal, labour-market and salary researchers.
- **Input:** keywords, location or geoId, date posted (24h / week / month), workplace (on-site / remote / hybrid), experience level, job type, company filter, max jobs (1 to 1,000 per run).
- **Output:** 32 fields per job: jobId, title, company, companyUrl, location, postedAt, applicantsText, applicantsCount, salaryText/Min/Max/Currency/Period, seniority, employmentType, jobFunction, industries, workplaceType, applyType, applyUrl, jobUrl, isActivelyHiring, seven company fields, description (and optional HTML), scrapedAt.
- **Extras:** dedupe across pages, new-jobs-only mode with a named key-value store, retries with exponential backoff, Apify Proxy with direct fallback.
- **Pricing:** pay per event. $0.0017 per job on the Apify free plan, $0.00085 per job on paid plans, plus a start event of $0.00005 per GB of run memory. Duplicates and already-seen jobs are never charged.
- **Limits:** public guest pages only; about 1,000 results per query; `jobFunction` is always null; salary only when the posting publishes it.

### Run in 30 seconds

1. Open the [prefilled run](https://console.apify.com/create-task-from-example/yQdsYyZ5Xcp9HtSwc) (free Apify account; it creates a saved task with a working input).
2. Press **Start**. The input is small on purpose, so the first run is quick and costs a few cents at most.
3. Open the **Output** tab, switch to Table view, download as CSV, Excel or JSON.

Prefilled input:

```json
{
  "keywords": "software engineer",
  "location": "United States",
  "maxItems": 10
}
```

Ready-made presets by use case (each opens as a saved task you can edit):

- [Remote data analyst jobs this week](https://console.apify.com/create-task-from-example/aMzTdIJfIMSKnyB23)
- [Entry level sales jobs in Detroit](https://console.apify.com/create-task-from-example/uajhdQ2EAYb5Ue8Tk)

### Why choose this over alternatives

- No login and no cookies: only the pages LinkedIn serves to logged-out visitors.
- Lower price per job on the free plan and on paid plans than the alternative we tested.
- Deduped across pages, with a new-jobs-only mode for schedules.
- On the 16 fields both tools return, fill rates were level in our test; see the table.

| | This actor | Typical alternatives |
|---|---|---|
| Rows returned for a 25-job request | 25 of 25 | 25 of 25 |
| Time for 25 jobs | 27 to 31 s | 81 s |
| Price per job, free plan | $0.0017 | $0.002 |
| Price per job, paid plans | $0.00085 | $0.001 |
| Fill rate on the 16 shared fields | 92.2% | 92.0% |
| Company logo present | 25 of 25 | not measured |

Measured 2026-10-11 (source: head-to-head test runs on the same query, "data analyst", United States, past week, 25 jobs each, one run per side, both runs succeeded). One run each is a small sample; your timings will vary with LinkedIn's rate limiting.

### What it does and who it is for

You describe a job search (keywords, place, filters). The actor pages through LinkedIn's public guest search, opens the public posting page for each job, optionally reads the public company page once per company, and writes one flat row per job to an Apify dataset. You can download the dataset as JSON, CSV, Excel, XML or HTML, or read it through the API.

It is built for people who need job listings as data:

- **Recruiters and staffing teams** tracking which companies are hiring for a role in a region.
- **Job boards and aggregators** that need fresh listings with a stable id for upserts.
- **Sales and research teams** that treat hiring as a signal (a company posting ten data roles is investing in data).
- **Salary and labour-market researchers** who want the pay ranges that postings publish, parsed into numbers.

It is not a profile scraper. It never reads member profiles and never outputs recruiter or poster names.

### Quick start

1. Enter keywords (for example `data analyst`) and a location.
2. Optionally narrow by date posted, remote/hybrid, level, job type or company.
3. Set Max jobs and run. Download as JSON, CSV or Excel, or call the API.

```json
{ "keywords": "data analyst", "location": "United States", "datePosted": "week", "workplace": ["remote"], "maxItems": 50 }
```

### What you get: output fields

Every row has the same 31 keys (32 when you ask for description HTML). A field is `null` when LinkedIn does not publish it for that job.

| Field | Type | Meaning |
|---|---|---|
| `jobId` | string | LinkedIn's job id. Stable, use it as your upsert key |
| `jobUrl` | string | Clean public job URL, tracking parameters removed |
| `title`, `company`, `location` | string | As shown on the public listing |
| `companyUrl` | string | Public LinkedIn company page URL |
| `postedAt` | string | ISO timestamp. LinkedIn publishes the date only, so the time part is midnight UTC |
| `applicantsText` | string | Text exactly as shown, for example "Over 200 applicants". Null when not shown |
| `applicantsCount` | number | The number parsed from `applicantsText` (200 for "Over 200 applicants"). Null when LinkedIn shows no count |
| `salaryText` | string | The pay text as posted, for example `$175,000.00/yr - $210,000.00/yr`. Null when the posting publishes no pay |
| `salaryMin`, `salaryMax` | number | Parsed from `salaryText` |
| `salaryCurrency`, `salaryPeriod` | string | For example `USD` and `year` |
| `seniority`, `employmentType` | string | The posting's own criteria, for example "Mid-Senior level", "Full-time". LinkedIn itself often shows "Not Applicable" for seniority |
| `jobFunction` | null | Always null today: LinkedIn no longer shows job function to logged-out visitors. The key is kept so your columns stay stable |
| `industries` | array | The posting's industries. Split from LinkedIn's sentence, so the last item can begin with "and" |
| `workplaceType` | string | `remote`, `hybrid` or `onsite`. Exact when you filter to one workplace type; otherwise inferred from title, location and description when stated, else null |
| `applyType` | string | `linkedin` (Easy Apply) or `external` |
| `applyUrl` | string | The employer's application URL when the public page exposes it, otherwise the LinkedIn job URL |
| `isActivelyHiring` | boolean | `true` when the listing carries LinkedIn's "actively hiring" badge, else null |
| `companyDescription`, `companyWebsite`, `companyIndustry` | string | From the public company page (needs `includeCompanyDetails`, on by default) |
| `companySize`, `companyHeadquarters` | string | For example "11-50 employees", "New York, New York" |
| `companyLogo` | string | Logo image URL, with a fallback to the logo on the search card |
| `companySlogan` | string | The company tagline. Null when the company publishes none |
| `companyAddress` | object | `{streetAddress, addressLocality, addressRegion, postalCode, addressCountry}` from the company page's structured data. Passed through as published |
| `description` | string | Plain-text job description. Null when `includeDescription` is off |
| `descriptionHtml` | string | Only when `includeDescriptionHtml` is on |
| `scrapedAt` | string | ISO timestamp of extraction |

### Sample output

A real row from a test run on 2026-10-11 (build 0.1.9). The two long text fields and the logo URL are shortened with `...`; nothing else is changed.

```json
{
  "jobId": "4438505532",
  "title": "Senior Data Analyst",
  "company": "Confido",
  "companyUrl": "https://www.linkedin.com/company/confidotech",
  "location": "New York, NY",
  "postedAt": "2026-10-03T00:00:00.000Z",
  "applicantsText": "Over 200 applicants",
  "salaryText": "$175,000.00/yr - $210,000.00/yr",
  "salaryMin": 175000,
  "salaryMax": 210000,
  "salaryCurrency": "USD",
  "salaryPeriod": "year",
  "seniority": "Not Applicable",
  "employmentType": "Full-time",
  "jobFunction": null,
  "industries": ["Software Development"],
  "workplaceType": null,
  "applyType": "external",
  "applyUrl": "https://www.linkedin.com/jobs/view/senior-data-analyst-at-confido-4438505532",
  "jobUrl": "https://www.linkedin.com/jobs/view/senior-data-analyst-at-confido-4438505532",
  "isActivelyHiring": null,
  "companyDescription": "Confido is the AI infrastructure powering CPG brands from deduction to production plan...",
  "companyLogo": "https://media.licdn.com/dms/image/v2/D4E0BAQH_cCz9VXIQ0A/company-logo_200_200/...",
  "companyWebsite": "https://www.confidotech.com",
  "companyIndustry": "Software Development",
  "companySize": "11-50 employees",
  "companyHeadquarters": "New York, New York",
  "companySlogan": "The AI Infrastructure for CPG Brands from Deduction to Production Plan",
  "companyAddress": {
    "streetAddress": null,
    "addressLocality": "New York",
    "addressRegion": "New York",
    "postalCode": "10001",
    "addressCountry": "US"
  },
  "description": "The brands on your kitchen shelf are run by people doing extraordinary work that nobody sees...",
  "scrapedAt": "2026-10-11T05:09:51.264Z"
}
```

Note what this row shows honestly: `applyType` is `external` but `applyUrl` is still the LinkedIn URL, because the public page did not expose the employer's link, and `workplaceType` is null because the search had no single workplace filter and the posting did not state one.

### Input options

No field is required by the form, but an empty input runs the example search `data analyst` in `United States`.

| Input | Default | What it does |
|---|---|---|
| `keywords` | none | Job title or skill, for example `registered nurse` |
| `location` | `United States` when no geoId is given | City, region or country as you would type it on LinkedIn, for example `Detroit, Michigan` |
| `geoId` | none | LinkedIn's exact location id, for example `103644278` for the United States. Use it with or instead of `location` when a place name is ambiguous |
| `datePosted` | `any` | `any`, `24h`, `week` or `month` |
| `workplace` | all | Any of `onsite`, `remote`, `hybrid` |
| `experienceLevel` | all | Any of `internship`, `entry`, `associate`, `mid-senior`, `director`, `executive` |
| `jobType` | all | Any of `full-time`, `part-time`, `contract`, `temporary`, `volunteer`, `internship`, `other` |
| `companyFilter` | any company | Numeric LinkedIn company ids are passed to LinkedIn's own filter. Company names or `linkedin.com/company/<slug>` URLs are matched against the company name on each listing |
| `searchUrls` | none | Paste LinkedIn job search URLs (or single `linkedin.com/jobs/view/...` URLs). The URL's keywords, location, geoId, date, workplace, experience, job type, company, distance, Easy Apply and under-10-applicants filters are used; anything the URL does not set falls back to the fields here |
| `splitByLocation` | none | List of locations. One search per location, results merged and deduplicated by job id, `maxItems` shared fairly between them |
| `distance` | 0 (LinkedIn default) | Radius in miles around a city-level location |
| `easyApplyOnly` | false | Only Easy Apply jobs |
| `under10Applicants` | false | Only jobs with fewer than 10 applicants so far |
| `companyIds` | none | Numeric LinkedIn company ids, filtered by LinkedIn itself |
| `minAnnualSalary` | 0 (off) | Keep only jobs whose published pay (converted to a yearly figure) is at least this number. Jobs without published pay are dropped when set, and dropped jobs are not charged |
| `maxItems` | 25 | Stop after this many jobs, 1 to 1,000 |
| `includeDescription` | true | Plain-text description. Same price either way |
| `includeDescriptionHtml` | false | Adds `descriptionHtml` with the original formatting |
| `includeCompanyDetails` | true | Adds the company fields. One extra request per unique company, cached for the run, skipped automatically when LinkedIn walls company pages |
| `onlyNewSinceLastRun` | false | Skip jobs delivered by earlier runs that share the same state store |
| `stateStoreName` | `linkedin-public-jobs-seen` | Named key-value store that remembers delivered job ids |
| `proxyConfiguration` | Apify Proxy | Falls back to a direct connection if the proxy keeps failing |

#### Filters & search URL

Set the filters on LinkedIn's jobs page, copy the address bar, and paste it into `searchUrls`. The actor reads the same filters from the URL and runs them through LinkedIn's public no-login endpoint. Example: `https://www.linkedin.com/jobs/search/?keywords=registered%20nurse&location=Detroit&f_WT=2&f_TPR=r604800&f_AL=true&distance=25`.

Checked against live LinkedIn responses on 2026-10-13: date posted, workplace type, experience level, job type, company id, distance, Easy Apply and under-10-applicants change the results. LinkedIn's public endpoint ignores its salary bucket (`f_SB2`) and sort (`sortBy`) parameters, so they are not sent. Use `minAnnualSalary` for a salary floor (applied by the actor) and read `postedAt` to sort recent-first yourself.

#### Input examples

Remote contract roles posted in the last 24 hours:

```json
{ "keywords": "react developer", "location": "United Kingdom", "datePosted": "24h", "workplace": ["remote"], "jobType": ["contract"], "maxItems": 100 }
```

Entry and associate roles at named companies. Names are matched as text against the listing's company name, so a name filter can scan many listings to find few matches; listings that do not match are not delivered and not charged:

```json
{ "keywords": "software engineer", "location": "United States", "experienceLevel": ["entry", "associate"], "companyFilter": ["Google", "https://www.linkedin.com/company/stripe"], "maxItems": 200 }
```

A saved search that returns only jobs it has not delivered before:

```json
{ "keywords": "data analyst", "location": "Detroit, Michigan", "datePosted": "week", "onlyNewSinceLastRun": true, "stateStoreName": "detroit-analyst-jobs", "maxItems": 100 }
```

### Pricing

Pay per event. There is no subscription and no rental fee for this actor.

| Event | Apify free plan | Paid plans (Bronze to Diamond) |
|---|---|---|
| Job extracted (one per job row delivered) | $0.0017 | $0.00085 |
| Actor start (one per GB of run memory, minimum one) | $0.00005 | $0.00005 |

The default run memory is 4 GB, so the start costs $0.0002 per run unless you lower the memory in the run options.

Worked examples at the default memory:

| Jobs delivered | Free plan | Paid plans |
|---|---|---|
| 25 (the form default) | 25 x $0.0017 + $0.0002 = $0.0427 | 25 x $0.00085 + $0.0002 = $0.0215 |
| 100 | $0.1702 | $0.0852 |
| 1,000 (the maximum for one run) | $1.7002 | $0.8502 |
| 10,000 (ten runs of 1,000) | $17.002 | $8.502 |

A daily schedule that finds 50 new jobs every day delivers 1,500 jobs in 30 days: 1,500 x $0.00085 + 30 x $0.0002 = $1.281 on a paid plan, or $2.556 on the free plan.

Apify's free plan includes $5 of monthly platform credit, which covers about 2,900 jobs with this actor at the free-plan price.

What is not charged: duplicates inside a run, jobs skipped by new-only mode, listings removed by a company-name filter, and jobs whose page has been taken down (HTTP 404 or 410). To cap spend, set **Maximum cost per run** in the run options; the actor checks the remaining budget before each job and stops cleanly when the limit is reached.

### How to run it

#### In Apify Console

Open the actor, fill in the form and press **Start**. Rows appear in the **Output** tab while the run is going. The status line shows progress as `delivered/requested` jobs. When the run ends, use **Export** for JSON, CSV, Excel, XML or HTML.

#### From the API or a client library

Set `APIFY_TOKEN` to your token from Apify Console > Settings > API & Integrations.

```bash
curl -X POST "https://api.apify.com/v2/acts/transparent_meteorite~linkedin-public-jobs/runs?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"keywords":"data analyst","location":"United States","datePosted":"week","maxItems":25}'
```

**Python** (`pip install apify-client`)

```python
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])
run_input = {"keywords": "data analyst", "location": "United States", "datePosted": "week", "maxItems": 25}
run = client.actor("transparent_meteorite/linkedin-public-jobs").call(run_input=run_input)
for job in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(job["jobId"], job["title"], job["company"], job["salaryText"])
```

**JavaScript / Node.js** (`npm install apify-client`)

```js
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('transparent_meteorite/linkedin-public-jobs').call({
  keywords: 'data analyst', location: 'United States', datePosted: 'week', maxItems: 25,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.length, 'jobs');
```

#### On a schedule

Create a schedule in Apify Console (Schedules > Create new), add this actor with your saved input, and pick a cron expression such as `0 7 * * *` for 07:00 every day. Turn on `onlyNewSinceLastRun` so each run returns, and charges for, only the jobs you have not received yet.

#### With n8n, Make, Zapier or AI agents

Start the actor with the Apify API (`POST /v2/acts/<actor>/runs`) or the Apify client in Python or JavaScript, wait for the run, then read `GET /v2/datasets/<id>/items`. The same input works as an n8n, Make or Zapier Apify node, and any MCP-capable agent can call it as an Apify tool. Every row has a stable `jobId` for upserts.

Webhooks work as for any Apify actor: add one for the "run succeeded" event and your endpoint receives the run id and dataset id as soon as the data is ready.

### New jobs only (monitor mode)

Turn on `onlyNewSinceLastRun` and keep the same `stateStoreName` between runs. The actor stores the id of every job it delivers in that named key-value store. On the next run, jobs already in the store are skipped before their page is fetched and are not charged.

- Use one store name per saved search, for example `detroit-analyst-jobs` and `remote-react-contracts`. Two searches sharing a store would hide each other's overlaps.
- Ids are kept for 90 days, then dropped.
- A job is marked as seen only after its row is pushed and charged, so an interrupted run does not lose jobs.
- The mode reports new jobs. It does not report jobs that were removed or edited.

### Run summary record

Each run writes a record named `OUTPUT` to its key-value store. It tells you what happened without reading the log:

| Key | Meaning |
|---|---|
| `query` | The search as the actor understood it |
| `pagesFetched`, `cardsSeen` | Search pages read and listings seen |
| `duplicates`, `filteredOut`, `skippedAlreadySeen` | Listings not delivered, and why |
| `detailFailures` | Job pages that could not be read after retries |
| `delivered`, `chargeCalls` | Rows written and events charged (these match) |
| `chargeLimitReached` | True when your maximum cost stopped the run |
| `proxy`, `http` | Proxy used, and request, retry, blocked and failure counts |
| `warnings` | Plain-language notes, for example that company pages were walled |

### Limits and honest caveats

- **Public guest pages only.** No login, no cookies, no private data. Anything LinkedIn shows only to signed-in members is out of reach, including job function, the exact applicant number and most external apply links.
- **About 1,000 results per query.** That is LinkedIn's own cap on the public search, and `maxItems` stops at 1,000 for the same reason. Split bigger searches by location, date window or job type.
- **Salary is there only when the posting publishes it.** Many postings publish no pay, and those rows have null salary fields. The actor never estimates a salary.
- **Company fields depend on the public company page.** In a 30-job test run, `companyAddress` was filled on 27 rows and `companySlogan` on 14; many companies publish no tagline. Some addresses carry LinkedIn's own placeholder text in `streetAddress` (for example "Headquarters"); it is passed through as published. If LinkedIn walls company pages for the run's IP, the company fields are skipped and a warning is written to `OUTPUT`.
- **A job whose detail page cannot be read is still delivered** with the search-card fields (title, company, location, posted date, URL) and null detail fields, and it is charged. `OUTPUT.detailFailures` counts these. Jobs that return 404 or 410 are dropped and not charged.
- **Rate limiting.** LinkedIn limits anonymous traffic. The actor retries each request up to 4 times with exponential backoff and rotates through the proxy. If LinkedIn keeps refusing, the run ends with the rows it has and a warning in `OUTPUT`; it never invents rows.
- **Order.** Rows come in LinkedIn's own result order for the query, not strictly newest first.

### FAQ

**Do I need a LinkedIn account or cookies?** No. The actor has no login step and sends no cookies.

**Why is `jobFunction` always null?** LinkedIn removed job function from the pages it serves to logged-out visitors. The column stays so that existing exports keep their shape.

**Why does `applyUrl` equal the LinkedIn URL when `applyType` is `external`?** The public page tells us the application happens on the employer's site but rarely exposes the link itself. `applyType` tells you which case you have.

**Can I get more than 1,000 jobs?** Yes, across several runs. Split the search by location (states, cities), by `datePosted` or by `jobType`, and merge on `jobId`.

**How fast is it?** A 25-job run took 27 to 31 seconds in our test on 2026-10-11, and a 30-job run with company details took 72 seconds. Speed depends on how hard LinkedIn is rate limiting at that moment.

**Does it return recruiter or poster names?** No. That is personal data and the actor does not collect it.

**How do I search one country precisely?** Use `geoId`. Free-text locations are interpreted by LinkedIn and can match a city of the same name elsewhere.

**Is the description complete?** Yes, the full public text. Turn on `includeDescriptionHtml` if you need the original formatting.

### Troubleshooting

| Symptom | Likely cause and fix |
|---|---|
| Zero rows and a warning that LinkedIn kept refusing requests | Rate limit on the run's IP. Run again later, or choose a different proxy group in the Proxy field |
| Fewer rows than `maxItems` | The search has fewer public results, a company-name filter removed listings (`OUTPUT.filteredOut`), new-only mode skipped them (`OUTPUT.skippedAlreadySeen`), or your maximum cost was reached |
| Company fields are all null | Company pages were walled for this run (see `OUTPUT.warnings`) or `includeCompanyDetails` is off |
| New-only run returns everything again | The `stateStoreName` changed, or the store was deleted. Keep the name identical between runs |
| Jobs from the wrong place | Ambiguous place name. Use `geoId` |

### Terms & responsible use

This actor requests only pages LinkedIn serves publicly to logged-out visitors. It does not log in, use cookies, bypass authentication or collect personal profiles. LinkedIn's User Agreement restricts automated access and scraping, and rules differ by country and by what you do with the data. You are responsible for how you use the output: respect LinkedIn's terms and applicable law (including privacy law), keep volumes modest, do not republish descriptions wholesale, and do not use the data to contact individuals. This is not legal advice. This actor is not affiliated with or endorsed by LinkedIn.

### More resources

- Guide & examples: [mamoo85.github.io/open-data-actors/linkedin-public-jobs.html](https://mamoo85.github.io/open-data-actors/linkedin-public-jobs.html)

### Works well with

Other actors from Detroit Web Agent - Open Data that pair with this one:

- [Career Site Jobs API (Greenhouse, Lever, Ashby and more)](https://apify.com/transparent_meteorite/ats-open-roles): cover employers that post on Greenhouse, Lever, Ashby and other career sites.
- [Workday Jobs Scraper](https://apify.com/transparent_meteorite/workday-jobs-scraper): cover employers that post on Workday career sites.

### Changelog

- 2026-10-09: First version (private): public guest search and job pages, salary parsing, dedupe, new-jobs-only mode.
- 2026-10-11: Company logo falls back to the search-card logo when the company page is walled (build 0.1.8).
- 2026-10-11: Added `companySlogan` and `companyAddress` from the public company page's structured data (build 0.1.9).
- 2026-10-12: README expanded: full field table, sample output, pricing examples, API and scheduling guides, limits, FAQ and troubleshooting.
- 2026-10-13: Added a Run in 30 seconds section with a one-click prefilled run link, saved use-case presets and cross-links to pairing actors.
- 2026-10-13: Added `searchUrls` (paste a LinkedIn search or job URL), `splitByLocation`, `distance`, `easyApplyOnly`, `under10Applicants`, `companyIds`, `minAnnualSalary` and the numeric `applicantsCount` field.

### Support

Found a job that should have been returned, a field that parsed wrongly, or need a field added? Open an issue on this actor's **Issues** tab with the run URL and what you expected. The run URL lets us see the exact input and the `OUTPUT` record.

### Need more than the data?

This actor is built by [Detroit Web Agent](https://www.detroitwebagent.com/?utm_source=apify\&utm_medium=readme\&utm_campaign=linkedin-public-jobs), a small Detroit studio that builds websites, lead pages and simple data tools for small businesses. If you want this data wired into a dashboard or an alert, get in touch.

***

Built and maintained by **Detroit Web Agent - Open Data** (Detroit, Michigan). Questions, bugs or a field you need: open an issue on this Actor's Issues tab and we will reply there.

### Use it from AI assistants (MCP)

LinkedIn Public Jobs Scraper works as a tool for AI assistants through the official Apify MCP server. Add this server URL to any MCP client (Claude Desktop, Claude Code, Cursor, ChatGPT connectors, VS Code):

```
https://mcp.apify.com/?actors=transparent_meteorite/linkedin-public-jobs
```

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com/?actors=transparent_meteorite/linkedin-public-jobs",
      "headers": { "Authorization": "Bearer <YOUR_APIFY_TOKEN>" }
    }
  }
}
```

# Actor input Schema

## `keywords` (type: `string`):

Job title or skill, e.g. "data analyst". Example: "registered nurse".

## `location` (type: `string`):

City, region or country as you would type it on LinkedIn, e.g. "Detroit, Michigan" or "United Kingdom".

## `geoId` (type: `string`):

Exact LinkedIn location id, e.g. 103644278 for the United States. Used together with or instead of the free-text location.

## `searchUrls` (type: `array`):

Paste one or more LinkedIn job search URLs (linkedin.com/jobs/search/?keywords=...\&location=...\&f_TPR=...) or single job URLs (linkedin.com/jobs/view/...). Filters in the URL (keywords, location, geoId, f_TPR, f_WT, f_E, f_JT, f_C, distance, f_AL, f_EA) override the matching fields below; anything the URL does not set uses the fields below. Example: \["https://www.linkedin.com/jobs/search/?keywords=nurse\&location=Detroit\&f_WT=2\&f_TPR=r604800"].

## `splitByLocation` (type: `array`):

Run one search per location and merge the results, deduplicated by job id; maxItems is shared across them. Example: \["Detroit, Michigan","Chicago, Illinois","Austin, Texas"].

## `datePosted` (type: `string`):

Only jobs posted within this window.

## `workplace` (type: `array`):

On-site, remote, hybrid (several allowed). Empty = all.

## `experienceLevel` (type: `array`):

Empty = all levels. Allowed: internship, entry, associate, mid-senior, director, executive. Example: \["entry","associate"].

## `jobType` (type: `array`):

Empty = all types. Allowed: full-time, part-time, contract, temporary, volunteer, internship, other. Example: \["full-time","contract"].

## `distance` (type: `integer`):

Search radius in miles around the location. 0 = LinkedIn default. Works with a city-level location. Example: 25.

## `easyApplyOnly` (type: `boolean`):

Only jobs you can apply to inside LinkedIn (Easy Apply).

## `under10Applicants` (type: `boolean`):

Only jobs with fewer than 10 applicants so far (LinkedIn's early-applicant filter).

## `minAnnualSalary` (type: `integer`):

Keep only jobs whose published pay, converted to a yearly figure, is at least this number. Jobs that publish no salary are dropped when this is set (LinkedIn's public endpoint has no salary filter, so this is applied by the actor; skipped jobs are not charged). 0 = off. Example: 100000.

## `companyFilter` (type: `array`):

LinkedIn company ids (digits, filtered by LinkedIn) or company names / linkedin.com/company/<slug> URLs (matched against the company name). Empty = any company. Example: \["Google","https://www.linkedin.com/company/stripe"].

## `companyIds` (type: `array`):

LinkedIn numeric company ids (filtered by LinkedIn itself), e.g. 1441 for Google. Combined with the Company filter field. Example: \["1441","1035"].

## `maxItems` (type: `integer`):

Stop after this many jobs (1 to 1000). You are charged per job delivered.

## `includeDescription` (type: `boolean`):

Plain-text job description. Same cost either way; turn off for a lighter dataset.

## `includeDescriptionHtml` (type: `boolean`):

Adds descriptionHtml with the original formatting.

## `onlyNewSinceLastRun` (type: `boolean`):

Skips jobs already delivered by earlier runs that share the same state store. Skipped jobs are not charged.

## `stateStoreName` (type: `string`):

Named key-value store remembering delivered job ids. Use a different name per saved search. Example: "detroit-analyst-jobs".

## `proxyConfiguration` (type: `object`):

Apify Proxy (datacenter by default). Falls back to a direct connection if the proxy fails.

## `includeCompanyDetails` (type: `boolean`):

Adds website, industry, size and headquarters from the public LinkedIn company page (one extra request per unique company, cached). Skipped automatically if LinkedIn walls it.

## Actor input object example

```json
{
  "keywords": "data analyst",
  "location": "United States",
  "datePosted": "any",
  "distance": 0,
  "easyApplyOnly": false,
  "under10Applicants": false,
  "minAnnualSalary": 0,
  "maxItems": 25,
  "includeDescription": true,
  "includeDescriptionHtml": true,
  "onlyNewSinceLastRun": false,
  "stateStoreName": "linkedin-public-jobs-seen",
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "includeCompanyDetails": true
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "keywords": "data analyst",
    "location": "United States",
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("transparent_meteorite/linkedin-public-jobs").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "keywords": "data analyst",
    "location": "United States",
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("transparent_meteorite/linkedin-public-jobs").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "keywords": "data analyst",
  "location": "United States",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call transparent_meteorite/linkedin-public-jobs --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,transparent_meteorite/linkedin-public-jobs"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/Jbm7eXafHL8Wsnjkb/builds/l15pi1Ol3lH6kOGgo/openapi.json
