# LinkedIn Jobs Scraper (No Cookies) ✅ Bulk Search by City (`crustapi/linkedin-jobs-scraper`) Actor

Scrape LinkedIn job listings in bulk: search by title, city, seniority and employment type. Get company, location, posting date, full description and direct job URLs. Up to 2,000 jobs per search. No login, no cookies, no LinkedIn account.

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

## Pricing

$1.00 / 1,000 job scrapeds

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

## What is LinkedIn Jobs Scraper?

Extract LinkedIn job listings in bulk, without a LinkedIn account. Search by job title and city, stack as many searches as you want, and download up to 2,000 jobs per search as a clean table. Every row arrives fully detailed: seniority, employment type, posting date and the full job description.

- 🔍 Any job title or keywords, any city or country, or search on filters alone with no keyword at all
- 🗺️ Run many searches in one go: every title runs once per location, all results in one dataset
- 🎯 Real filters, applied at the source: exclude title words (senior, intern...), require words in the job description, filter by employment type, seniority, posting window, or a single company
- 📄 Full job description, seniority, employment type, job function and industry on every row
- 🧲 Recruiter attached: keep only jobs where a named recruiter you can message posted the role
- 🗓️ Newest first by default, with a calibrated posting-date range on every job
- 🔑 No login, no cookies, no LinkedIn account, nothing to get restricted
- ⚡ Thousands of jobs per minute
- 📦 Export as JSON, CSV, Excel, HTML, or XML

### What data do I get?

| Field | Example |
|---|---|
| Job title | Software Engineer, Simulation |
| Company + LinkedIn URL | Bedrock Robotics · linkedin.com/company/bedrock-robotics |
| Location | San Francisco, CA |
| Structured location | city San Francisco · state CA |
| Posted | "1 day ago", plus a calibrated date range (postedAfter 2026-08-25, postedBefore 2026-08-27) |
| Seniority | Entry level, Mid-Senior level, Director... |
| Employment type | Full-time, Contract, Part-time, Internship... |
| Job function | Engineering and Information Technology |
| Industries | Robotics Engineering |
| Full description | Clean text, up to 4,000 characters |
| Job poster | Name, headline and profile URL of the recruiter, when the posting names one (about 1 in 5 jobs) |
| Job URL + job ID | Direct link · 4439948814 |
| Source search | Which of your searches found it (a job matched by two searches is kept once) |

**Good to know**

- Every row carries the deep fields: seniority, employment type, job function, industries, posting age and the full description text. No thin rows.
- LinkedIn publishes a posting's age as a rounded phrase ("2 weeks ago"), never an exact date. We convert it to an honest date range (postedAfter / postedBefore) instead of inventing a fake exact date.
- Any single field can be blank when the posting does not publish it, so check for nulls rather than assuming every column is filled.
- Most big cities work as a bare name (London, Berlin, Toronto, Sydney). If a city returns nothing, add the country ("Amsterdam, Netherlands") and run it again.

### Pricing

**$1.00 per 1,000 jobs** ($0.001 per job), plus a small Apify compute fee. Empty searches and failed fetches are never charged.

| Run | Actor cost |
|---|---|
| 1,000 jobs | $1.00 |
| 10,000 jobs | $10 |
| 100,000 jobs | $100 |

Volume comes from stacking titles and locations: every title runs once per location, and each search returns up to 2,000 jobs, well past the ~300 rows LinkedIn's own search page gives up after.

### How it works

The form works like a job search should: filters, grouped the way you think about them.

1. **Job title**: add titles or keywords, one per row, and optionally exclude words (senior, intern...). Each row runs as its own search. You can also leave it empty and search on filters alone.
2. **Location**: add cities or regions, one per row. Every title runs once per location, so 3 titles and 4 cities is 12 searches in one run. Exclude places the same way.
3. Narrow further, all applied at the source so your result count stays full: **Job description contains**, **Employment type**, **Seniority**, **Posting date** (past 24 hours, week, month, or at most N days), **Recruiter attached**, or a single **Company**.
4. **Results**: cap jobs per search (0 takes everything, up to 2,000 per search). Newest first by default.
5. Click **Start**, then download JSON, CSV, or Excel.

Works great on a schedule: pull fresh listings daily and webhook them anywhere.

### Output sample

Real output from a real run:

```json
{
  "jobId": "4439948814",
  "jobUrl": "https://www.linkedin.com/jobs/view/4439948814",
  "title": "Software Engineer, Simulation",
  "company": "Bedrock Robotics",
  "companyUrl": "https://www.linkedin.com/company/bedrock-robotics",
  "location": "San Francisco, CA",
  "locationParsed": {
    "city": "San Francisco",
    "state": "CA",
    "raw": "San Francisco, CA"
  },
  "postedAgeText": "1 day ago",
  "postedAfter": "2026-08-25",
  "postedBefore": "2026-08-27",
  "seniority": "Not Applicable",
  "employmentType": "Full-time",
  "jobFunction": "Engineering and Information Technology",
  "jobFunctions": ["Engineering and Information Technology"],
  "industries": "Robotics Engineering",
  "description": "Join the team bringing advanced autonomy to the built world. At Bedrock, we're moving AI out of the lab and into the real world...",
  "searchKeywords": "software engineer",
  "searchLocation": "San Francisco"
}
```

Recruiters use it to track open roles in a niche. Sales teams use it for lead generation and prospecting, a company posting ten engineering roles is a buying signal. Job boards use it to stay stocked with fresh listings.

### LinkedIn Jobs API

Everything this actor does is available programmatically: start runs, pass searches and filters, and read results through the Apify API, or through any Apify integration (Zapier, Make, n8n, Google Sheets, webhooks). If you want a direct LinkedIn jobs API with the same data outside Apify, the same engine powers [crustapi.com](https://crustapi.com/linkedin-jobs-scraper).

### FAQ

**Is this legal?** The actor only reads publicly visible listings, the same pages you can open in a logged-out browser. No login, no private data.

**Do I need a LinkedIn account?** No. Nothing runs through an account, so there is nothing to restrict.

**Why don't I see remote filters?** LinkedIn does not publish workplace type (remote, hybrid, on-site) on public job pages. Tools that offer that filter are guessing from the description, and the guesses are often wrong. We would rather give you a filter that works: put "remote" in **Job description contains** instead.

**Can I use the API?** Yes, runs and results are fully accessible through the Apify API and all Apify integrations.

### More from CrustAPI

| Actor | What it does |
|---|---|
| LinkedIn Company Scraper | Size, industry, HQ, employee count, numeric IDs |
| LinkedIn Company Posts Scraper | Latest posts from any company page |
| LinkedIn Profile Posts Scraper | Posts from any person's profile |
| LinkedIn Profile Scraper + Email Finder | Public profiles plus verified work emails |
| Google Maps Scraper | Every business in any area |

Need volume beyond Apify, like tracking millions of jobs or profiles? See [crustapi.com/wholesale](https://crustapi.com/wholesale).

### Support and feedback

We ship improvements weekly, and user reports drive the roadmap. Something broken or missing?

- Open an issue on this actor's **Issues** tab in Apify Console
- Or email us at <support@crustapi.com>

***

*This actor is an independent tool built by CrustAPI. It is not affiliated with, endorsed by, or sponsored by LinkedIn Corporation. LinkedIn is a registered trademark of LinkedIn Corporation, and all other trademarks belong to their owners.*

# Actor input Schema

## `titleKeywords` (type: `array`):

Job titles or keywords, one per row. Each row runs as its own search, exactly like the chips in the CrustAPI dashboard. Leave empty to search by filters alone.

## `titleExclude` (type: `string`):

Drop any job whose title contains one of these words. Comma-separated, for example: senior, director, intern.

## `descriptionKeywords` (type: `string`):

Only keep jobs whose description mentions at least one of these. Comma-separated, for example: python, remote, visa sponsorship.

## `locations` (type: `array`):

Cities or regions, one per row. Each keyword above runs once per location, like the dashboard. Leave empty for everywhere.

## `locationExclude` (type: `string`):

Drop jobs in these places. Semicolon-separated because place names contain commas, for example: New York, NY; San Francisco, CA.

## `employmentType` (type: `array`):

Only these employment types. Leave empty for all.

## `seniority` (type: `array`):

Only these seniority levels. Leave empty for all.

## `hasRecruiter` (type: `string`):

Jobs where the poster is a named recruiter you can message. About 1 in 5 jobs publishes one.

## `postedWithin` (type: `string`):

Only return jobs posted in this window. LinkedIn applies this at the source, so you still get full pages of results.

## `daysSincePostedMax` (type: `integer`):

Finer-grained than "Posted within": for example 3 keeps only jobs posted in the last 3 days. Leave empty to skip.

## `company` (type: `string`):

Optional. Limit results to one company, by its LinkedIn company URL or slug (for example stripe). Leave blank for all companies.

## `maxJobsPerSearch` (type: `integer`):

Cap how many jobs each search returns. Served from our continuously-updated index, a broad search can page far past LinkedIn’s own ~300-row ceiling. Each page of up to 100 jobs is one API call. Use 0 for the maximum (2,000 per search).

## `newestFirst` (type: `boolean`):

Results come newest-first by default. Untick to keep the same order (this switch is kept for compatibility).

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

Legacy input from the first release: keyword and location pairs. Still honored if present; superseded by Job title + Location above.

## Actor input object example

```json
{
  "titleKeywords": [
    "software engineer"
  ],
  "locations": [
    "San Francisco"
  ],
  "hasRecruiter": "any",
  "postedWithin": "any",
  "maxJobsPerSearch": 50,
  "newestFirst": false
}
```

# Actor output Schema

## `results` (type: `string`):

One row per job: title, company, location, seniority, employment type, posting age, description and a direct job URL.

# 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 = {
    "titleKeywords": [
        "software engineer"
    ],
    "locations": [
        "San Francisco"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("crustapi/linkedin-jobs-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 = {
    "titleKeywords": ["software engineer"],
    "locations": ["San Francisco"],
}

# Run the Actor and wait for it to finish
run = client.actor("crustapi/linkedin-jobs-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 '{
  "titleKeywords": [
    "software engineer"
  ],
  "locations": [
    "San Francisco"
  ]
}' |
apify call crustapi/linkedin-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,crustapi/linkedin-jobs-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/g69YYf0FMcg1kMLF3/builds/EaOM7i9Ifj5A9nxOb/openapi.json
