# Fast LinkedIn Jobs Scraper — $0.50 per 1,000 (`northbell/linkedin-jobs-fast-scraper`) Actor

Bulk LinkedIn job listings at ten jobs per request: title, company, location and the exact posting date. Built for volume and speed — no login, no cookies, no per-job page fetch.

- **URL**: https://apify.com/northbell/linkedin-jobs-fast-scraper.md
- **Developed by:** [Northbell](https://apify.com/northbell) (community)
- **Categories:** Jobs, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Fast LinkedIn Jobs Scraper — $0.50 per 1,000

Bulk LinkedIn job listings, cheap and quick: title, company, location, **the exact posting date**, and links to both the job and the company.

Ten jobs per request. 1,000 jobs is 100 requests, not 1,100.

### Why this one is fast

Most LinkedIn job scrapers open every job's own page to read its description and applicant count. That is one HTTP request **per job** — accurate, and ten times the work.

This Actor reads only the search results, which arrive ten at a time:

| | requests for 1,000 jobs |
|---|---|
| per-job scrapers | ~1,100 |
| **this one** | **100** |

Same job list. A tenth of the requests, a tenth of the time, and a price that reflects it.

**If you need applicant counts**, use the sibling Actor — [LinkedIn Jobs Scraper with Applicant Counts](https://apify.com/northbell/linkedin-jobs-applicants-scraper). It opens each job page, so it costs more and takes longer, and in exchange it tells you how many people applied and how fast that number is climbing. Different jobs, different tools.

### The date is a real date

LinkedIn's search results show "3 days ago". This Actor reads the machine-readable `datetime` attribute sitting behind that text and returns `2026-08-20`.

Relative dates rot the moment you store them. A dataset full of "3 days ago" is worthless a week later; a dataset full of ISO dates is still true.

### What you get

**`job`** — one row per job.

| field | meaning |
|---|---|
| `title`, `company`, `location` | the posting |
| `postedOn` | exact date, `YYYY-MM-DD` |
| `activelyHiring` | LinkedIn's "Actively Hiring" badge |
| `jobUrl` | direct link to the job |
| `companySlug`, `companyUrl` | the company's LinkedIn page — normalised, so a UK posting linking to `uk.linkedin.com` still comes back as a usable handle |
| `searchKeywords`, `searchLocation` | which of your searches surfaced it |

**`error`** — anything that failed, written where you will actually see it.

### Several searches in one run

```json
{
  "searches": [
    { "keywords": "backend engineer", "location": "Berlin" },
    { "keywords": "data engineer", "location": "Munich" },
    { "keywords": "platform engineer", "location": "Remote" }
  ],
  "maxJobs": 300,
  "datePosted": "past-24-hours"
}
```

One run instead of three: one start charge instead of three, one rate-limit budget instead of three competing ones.

**The same job often appears in several searches.** By default you receive it — and pay for it — once, and `searchKeywords` tells you which search found it first. Set `dedupeAcrossSearches: false` if you would rather see every occurrence.

### Three things it gets right

**It never charges you twice for the same job.** Deduplication happens before delivery, not after, so the count you are billed for is the count of distinct rows you received.

**It stops exactly at `maxJobs`.** Pages arrive ten at a time, so a naive implementation asked for 25 and delivers 30. This one delivers 25.

**A failed fetch becomes a row, not a log line.** Nobody reads run logs. Failures land in the dataset as `error` rows, and the run is marked failed when nothing came back at all. A 429 is retried with backoff before it is called a failure, because "too many right now" is not the same as "this search is broken".

### No login. Not as a policy — as a property of the code.

This Actor never signs in, never asks you for a session cookie, and never sends one. It reads LinkedIn's public guest job endpoint, the same results an anonymous visitor sees.

That is enforced, not promised:

- The request headers are a **frozen object** with no `Cookie` and no `Authorization` field, and nothing can add one at runtime.
- A guard rejects any attempt to attach a credential header, and **the input schema refuses any field** whose name looks like `cookie`, `session`, `token`, `auth` or `password`.
- Unit tests assert all of the above.

If a competing tool asks you to paste your `li_at` cookie, it is operating your LinkedIn account on your behalf. This one cannot, by construction.

### What you pay for

| event | price |
|---|---|
| Actor start | $0.01 per run |
| Job | $0.0005 — **$0.50 per 1,000** |

Duplicates and failed fetches are not charged.

### Limits worth knowing

- **No description, no applicant count, no salary.** Those live on each job's own page, and fetching them is exactly what this Actor skips. The sibling Actor gets them.
- **Guest search results thin out** several hundred results deep; LinkedIn does not serve unlimited pages to anonymous visitors.
- The rate-limit budget persists in a key-value store, so overlapping runs of this Actor share one budget rather than stacking up.

### On data and privacy

This Actor collects **job postings**, not people. No applicant identities, no profiles, no contact details.

It keeps **no history** — that is part of why it is cheap. The only thing written to storage is the shared rate-limit counter.

### Running locally

```bash
npm install
npm test          # 22 unit tests, no network, including the no-login guarantees
```

# Actor input Schema

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

What to search for, exactly as you would type it into LinkedIn's job search. Ignored if you fill in Searches below.

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

City, region or country as LinkedIn spells it — "United States", "Berlin, Germany", "Remote".

## `searches` (type: `array`):

Run many searches in one go: \[{"keywords":"backend engineer","location":"Berlin"},{"keywords":"data engineer","location":"Munich"}]. Cheaper and faster than one run each. Overrides the two fields above.

## `maxJobs` (type: `integer`):

10 jobs per request, so 1,000 jobs is 100 requests.

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

Narrowing to the last 24 hours is the usual choice for a daily run.

## `workType` (type: `string`):

LinkedIn's workplace filter.

## `experience` (type: `string`):

LinkedIn's seniority filter.

## `dedupeAcrossSearches` (type: `boolean`):

The same job often appears in several searches. On by default so you receive it — and pay for it — once. Turn it off if you need to know which search surfaced it.

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

Optional. LinkedIn's internal location id, taken from a job-search URL. More precise than a location name.

## `maxRequestsPerMinute` (type: `integer`):

Kept polite by default. The budget is shared across your runs of this Actor, so parallel runs do not stack up.

## Actor input object example

```json
{
  "keywords": "software engineer",
  "location": "United States",
  "maxJobs": 100,
  "datePosted": "any",
  "workType": "any",
  "experience": "any",
  "dedupeAcrossSearches": true,
  "maxRequestsPerMinute": 30
}
```

# Actor output Schema

## `all` (type: `string`):

Every row: job and error.

## `jobs` (type: `string`):

One row per job: title, company, location, exact posting date, and links to both the job and the company.

# 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": "software engineer",
    "location": "United States"
};

// Run the Actor and wait for it to finish
const run = await client.actor("northbell/linkedin-jobs-fast-scraper").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": "software engineer",
    "location": "United States",
}

# Run the Actor and wait for it to finish
run = client.actor("northbell/linkedin-jobs-fast-scraper").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": "software engineer",
  "location": "United States"
}' |
apify call northbell/linkedin-jobs-fast-scraper --silent --output-dataset

```

## MCP server setup

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

```

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/igHOaBskO1dtEPEhE/builds/nFQbuRi8XxNjXWyhL/openapi.json
