# Indeed Jobs Scraper (`mukeshrana90/indeed-jobs-scraper`) Actor

Extract detailed job listings from Indeed by search query + filters or by URL. Returns ~60 structured fields per job plus cross-run deduplication, skill/seniority extraction, and normalized salaries.

- **URL**: https://apify.com/mukeshrana90/indeed-jobs-scraper.md
- **Developed by:** [Mukesh Kumar](https://apify.com/mukeshrana90) (community)
- **Categories:** Jobs, Automation
- **Stats:** 4 total users, 2 monthly users, 100.0% runs succeeded, NaN bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 results

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use 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

## Indeed Jobs Scraper — Extract Job Listings, Salaries & Skills from Indeed.com

**Indeed Jobs Scraper** is a fast, structured **Indeed scraper** that extracts job postings from **Indeed.com** and 50+ Indeed country domains (UK, Canada, Australia, India, Germany, France, and more). Search by keyword + filters or paste any Indeed URL and get **~60 structured fields per job** as JSON, CSV, Excel, or XML — complete with **salary normalization**, **skills extraction**, **seniority detection**, and **cross-run deduplication** for scheduled monitoring.

Use it to build job boards, recruiter dashboards, salary benchmarks, hiring intelligence, and ATS pipelines — without writing a single line of scraping code.

> Not affiliated with, endorsed by, or sponsored by Indeed. Scrape responsibly and in compliance with applicable laws and Indeed's terms of service.

---

### Why this Indeed scraper

- **Two input modes** — supply a list of **Indeed URLs** *or* a **search query + country + filters**. Mix-and-match for any use case.
- **50+ country domains** — `indeed.com`, `uk.indeed.com`, `ca.indeed.com`, `de.indeed.com`, `au.indeed.com`, `in.indeed.com`, and more.
- **Full job details** — title, company, location, salary, benefits, full description (text + optional HTML), apply URL, posting date, urgent-hire signals.
- **Skills & seniority extraction** — automatic parsing of `skills[]`, `detectedSeniority` (entry / mid / senior), and `minYearsExperience`.
- **Normalized salaries** — annualized min/max converted to one **target currency** (USD, EUR, GBP, INR, CAD, AUD, and more) with optional FX overrides.
- **Cross-run deduplication** — emit *only new* jobs since the last run, perfect for **scheduled job monitoring** (hourly / daily / weekly).
- **Webhook delivery** — push new postings to Slack, Make, Zapier, n8n, or your own endpoint on every run finish.
- **Cloudflare-aware** — fingerprinted browser over Apify **residential proxies** with session rotation and challenge detection.
- **Exports everywhere** — JSON, CSV, Excel, XML, HTML, or live via the **Apify API**.

---

### Use cases

| Use case | What you get |
|---|---|
| **Job board / aggregator** | A live feed of Indeed listings with full details, refreshed on a schedule |
| **Recruitment intelligence** | Track competitor hiring — which companies post which roles, where, at what salary |
| **Salary benchmarking** | Normalized annual min/max across roles, locations, and seniority levels |
| **ATS / sourcing pipelines** | Push new postings into your ATS, CRM, or Airtable via webhook + API |
| **Market & labor analytics** | Build dashboards on hiring volume, remote-work trends, in-demand skills |
| **Lead generation** | Find companies that are actively hiring for specific tech stacks or roles |
| **Personal job alerts** | Cross-run dedupe + webhook = a private, customized job alert in Slack/email |

---

### Quick start

#### Option A — Scrape by Indeed URL

Paste any Indeed search, company, or filtered URL:

```json
{
  "urls": [
    "https://www.indeed.com/jobs?q=software+engineer&l=Remote",
    "https://www.indeed.com/cmp/Google/jobs",
    "https://uk.indeed.com/jobs?q=data+scientist&l=London"
  ],
  "maxRowsPerUrl": 50,
  "scrapeJobDetails": true
}
````

#### Option B — Scrape by search query + filters

```json
{
  "query": "data analyst",
  "country": "us",
  "location": "New York, NY",
  "radius": "25",
  "jobType": "fulltime",
  "level": "mid_level",
  "sort": "date",
  "fromDays": "7",
  "remote": "remote",
  "maxRows": 100,
  "scrapeJobDetails": true,
  "extractSkills": true,
  "normalizeSalary": true,
  "targetCurrency": "USD"
}
```

***

### Input schema

Provide **either** `urls` (Option A) **or** `query` + `country` (Option B). All other fields are optional.

#### Option A — by URL

| Field | Type | Default | Description |
|---|---|---|---|
| `urls` | `string[]` | `[]` | Indeed search, company, or filtered URLs. When set, query-based fields are ignored. |
| `maxRowsPerUrl` | `integer` | `100` | Maximum jobs to scrape per URL. `0` = no limit. |

#### Option B — by search parameters

| Field | Type | Default | Description |
|---|---|---|---|
| `query` | `string` | — | Job keywords, e.g. `"data analyst"`. Required when not using `urls`. |
| `country` | `enum` | `us` | Indeed country domain. 50+ options including `us`, `gb`, `ca`, `au`, `in`, `de`, `fr`, `es`, `it`, `nl`, `br`, `mx`, `jp`, `sg`, `ae`, and more. |
| `location` | `string` | — | City, state, or postcode, e.g. `"San Francisco, CA"`. |
| `radius` | `enum` | — | Miles from `location`: `0`, `5`, `10`, `15`, `25`, `35`, `50`, `100`. |
| `jobType` | `enum` | — | `fulltime`, `parttime`, `contract`, `internship`, `temporary`, `permanent`, `seasonal`, `freelance`. |
| `level` | `enum` | — | `entry_level`, `mid_level`, `senior_level`. |
| `sort` | `enum` | `relevance` | `relevance` or `date` (newest first). |
| `fromDays` | `enum` | — | Posted within `1`, `3`, `7`, or `14` days. |
| `remote` | `enum` | — | `remote` or `hybrid`. |
| `maxRows` | `integer` | `100` | Total cap across all pages. `0` = no limit. |
| `includeSimilarJobs` | `boolean` | `true` | Include Indeed's "similar to your search" suggestions. |

#### Enrichment & advanced

| Field | Type | Default | Description |
|---|---|---|---|
| `scrapeJobDetails` | `boolean` | `true` | Visit each job page for the full description, benefits, apply URL, and company profile. Disable for a faster listing-only run. |
| `includeDescriptionHtml` | `boolean` | `false` | Include the raw HTML job description in addition to plain text. |
| `extractSkills` | `boolean` | `true` | Parse the description into `skills[]`, `detectedSeniority`, and `minYearsExperience`. |
| `normalizeSalary` | `boolean` | `true` | Add `normalizedSalary{}` — annualized min/max in a single target currency. |
| `targetCurrency` | `enum` | `USD` | Output currency: `USD`, `EUR`, `GBP`, `CAD`, `AUD`, `INR`, `SGD`, `ZAR`, `BRL`, `MXN`, `AED`, `JPY`, `CHF`, `SEK`, `PLN`. |
| `fxRatesOverride` | `object` | `{}` | Override built-in FX rates (units per 1 USD), e.g. `{"EUR": 0.92, "GBP": 0.79}`. |
| `deduplicate` | `boolean` | `true` | Drop duplicate `jobKey`s within this run. |
| `onlyNewSinceLastRun` | `boolean` | `false` | **Cross-run dedupe** — persist seen `jobKey`s and emit only postings unseen in prior runs. |
| `dedupeStoreName` | `string` | `indeed-seen-jobs` | Named key-value store used for cross-run deduplication. |
| `notifyWebhookUrl` | `string` | — | Optional. POST a JSON summary of the run's jobs to this URL on finish. Secret. |
| `maxConcurrency` | `integer` | `5` | Max parallel browser pages (1–25). Lower = gentler on anti-bot systems. |
| `proxyConfiguration` | `object` | `RESIDENTIAL` | Proxy settings. **Residential proxies strongly recommended** — Indeed blocks datacenter IPs. |

***

### Output schema

One record per job is pushed to the default dataset. Below is the full structure with all fields and types.

#### Example output record

```json
{
  "jobKey": "a1b2c3d4e5f67890",
  "title": "Senior Data Analyst",
  "companyName": "Acme Corp",
  "companyUrl": "https://www.indeed.com/cmp/Acme-Corp",
  "companyLogoUrl": "https://d2q79iu7y748jz.cloudfront.net/s/_logo/acme.png",
  "companyRating": 4.1,
  "companyReviewCount": 320,
  "city": "New York",
  "state": "NY",
  "postalCode": "10001",
  "formattedLocation": "New York, NY 10001",
  "isRemote": false,
  "jobType": "Full-time",
  "salary": {
    "salaryText": "$90,000 - $120,000 a year",
    "salaryMin": 90000,
    "salaryMax": 120000,
    "currency": "USD",
    "period": "year"
  },
  "benefits": ["Health insurance", "401(k)", "Paid time off"],
  "descriptionText": "We are looking for a Senior Data Analyst with 5+ years of SQL and Python experience...",
  "descriptionHtml": "<p>We are looking for...</p>",
  "datePublished": "2026-05-12T00:00:00.000Z",
  "age": "3 days ago",
  "sponsored": false,
  "hiringDemand": {
    "isUrgentHire": true,
    "isHighVolumeHiring": false
  },
  "jobUrl": "https://www.indeed.com/viewjob?jk=a1b2c3d4e5f67890",
  "applyUrl": "https://www.indeed.com/applystart?jk=a1b2c3d4e5f67890",
  "expired": false,

  "skills": ["SQL", "Python", "Tableau", "Data Analysis", "Statistics"],
  "detectedSeniority": "senior",
  "minYearsExperience": 5,

  "normalizedSalary": {
    "annualMin": 90000,
    "annualMax": 120000,
    "currency": "USD",
    "sourceCurrency": "USD",
    "sourcePeriod": "year",
    "fxRateApplied": 1,
    "isEstimatedFromText": false
  },

  "source": "indeed",
  "scrapedAt": "2026-05-18T10:00:00.000Z"
}
```

#### Field reference

##### Core fields

| Field | Type | Description |
|---|---|---|
| `jobKey` | `string` | Indeed's unique job identifier (used in `viewjob?jk=…`). |
| `title` | `string` | Job title. |
| `companyName` | `string` | Hiring company. |
| `companyUrl` | `string` | Indeed company profile URL. |
| `companyLogoUrl` | `string \| null` | Company logo image URL. |
| `companyRating` | `number \| null` | Company rating on Indeed (0–5). |
| `companyReviewCount` | `number \| null` | Number of Indeed reviews. |
| `city` | `string \| null` | City. |
| `state` | `string \| null` | State / region code. |
| `postalCode` | `string \| null` | Postal / ZIP code. |
| `formattedLocation` | `string` | Human-readable location string. |
| `isRemote` | `boolean` | Whether the role is remote / hybrid. |
| `jobType` | `string \| null` | Employment type — `Full-time`, `Contract`, etc. |

##### Salary, benefits, description

| Field | Type | Description |
|---|---|---|
| `salary.salaryText` | `string \| null` | Raw salary text from Indeed. |
| `salary.salaryMin` | `number \| null` | Parsed minimum (source currency, source period). |
| `salary.salaryMax` | `number \| null` | Parsed maximum (source currency, source period). |
| `salary.currency` | `string \| null` | Source currency code (e.g. `USD`, `GBP`, `EUR`). |
| `salary.period` | `string \| null` | Pay period — `hour`, `day`, `week`, `month`, `year`. |
| `benefits` | `string[]` | Listed benefits, e.g. `["Health insurance", "401(k)"]`. |
| `descriptionText` | `string` | Plain-text job description. |
| `descriptionHtml` | `string \| null` | Raw HTML description (only when `includeDescriptionHtml: true`). |

##### Dates, metadata, links

| Field | Type | Description |
|---|---|---|
| `datePublished` | `string (ISO 8601)` | When the job was first posted on Indeed. |
| `age` | `string` | Human-readable age, e.g. `"3 days ago"`. |
| `sponsored` | `boolean` | Whether this is a sponsored / promoted listing. |
| `hiringDemand.isUrgentHire` | `boolean` | Indeed's "Urgently hiring" badge. |
| `hiringDemand.isHighVolumeHiring` | `boolean` | High-volume hiring signal. |
| `jobUrl` | `string` | Canonical Indeed job URL. |
| `applyUrl` | `string \| null` | Direct apply URL (Indeed Apply or external ATS). |
| `expired` | `boolean` | Whether the posting is expired / no longer accepting applications. |
| `source` | `string` | Always `"indeed"`. |
| `scrapedAt` | `string (ISO 8601)` | Timestamp of when this record was collected. |

##### Enrichment fields (when enabled)

| Field | Type | Description |
|---|---|---|
| `skills` | `string[]` | Detected skills from title + description (e.g. `SQL`, `Python`, `AWS`, `React`). |
| `detectedSeniority` | `string \| null` | `entry`, `mid`, or `senior`. |
| `minYearsExperience` | `number \| null` | Minimum years of experience parsed from the description. |
| `normalizedSalary.annualMin` | `number \| null` | Annualized minimum in `targetCurrency`. |
| `normalizedSalary.annualMax` | `number \| null` | Annualized maximum in `targetCurrency`. |
| `normalizedSalary.currency` | `string` | Output currency (defaults to `USD`). |
| `normalizedSalary.sourceCurrency` | `string` | Original currency on the posting. |
| `normalizedSalary.sourcePeriod` | `string` | Original period (`hour`, `year`, etc.). |
| `normalizedSalary.fxRateApplied` | `number` | FX rate used for conversion. |
| `normalizedSalary.isEstimatedFromText` | `boolean` | `true` if the salary was parsed from free-text vs structured data. |

***

### Scheduled monitoring — get only new Indeed jobs

Set up an automated **Indeed job monitor** that delivers only fresh postings:

1. Enable `"onlyNewSinceLastRun": true` and pick a `dedupeStoreName`.
2. Set `"notifyWebhookUrl"` to your Slack incoming webhook, Make/Zapier scenario, n8n workflow, or any HTTPS endpoint.
3. Schedule the actor (hourly / daily) from the **Apify Console → Schedules**.
4. Each run's dataset (and webhook payload) contains **only** postings unseen in prior runs.

#### Webhook payload (`POST`, `application/json`)

```json
{
  "event": "indeed.jobs.scraped",
  "finishedAt": "2026-05-18T10:00:00.000Z",
  "runId": "abc123",
  "datasetId": "xyz789",
  "datasetItemsUrl": "https://api.apify.com/v2/datasets/xyz789/items?format=json",
  "source": {
    "query": "data analyst",
    "country": "us",
    "location": "New York, NY"
  },
  "monitoring": true,
  "totalJobs": 18,
  "delivered": 18,
  "truncated": false,
  "jobs": [
    {
      "title": "Senior Data Analyst",
      "companyName": "Acme Corp",
      "salaryText": "$90,000 - $120,000 a year",
      "normalizedSalary": {
        "annualMin": 87692,
        "annualMax": 131538,
        "currency": "USD"
      },
      "skills": ["SQL", "Python"],
      "jobUrl": "https://www.indeed.com/viewjob?jk=...",
      "applyUrl": "https://..."
    }
  ]
}
```

Up to **200 jobs** are inlined; if `truncated` is `true`, fetch the rest from `datasetItemsUrl`. Test with a free [webhook.site](https://webhook.site) URL before connecting production endpoints.

***

### Output formats & integrations

Pull results as:

- **JSON** — `https://api.apify.com/v2/datasets/{DATASET_ID}/items?format=json`
- **CSV** — `?format=csv`
- **Excel** — `?format=xlsx`
- **XML / HTML / JSONL** — same dataset endpoint, different `format` param

Native integrations: **Make**, **Zapier**, **n8n**, **Slack**, **Airtable**, **Google Sheets**, **Pipedream**, plus generic webhooks.

***

### Pricing

This actor is priced per scraped job result via Apify's built-in pay-per-result billing.

- **Measured Apify usage** — approximately **$6–7 per 1,000 jobs** of platform cost (browser compute + residential proxy bandwidth).
- **Recommended retail price** — **$8–12 per 1,000 jobs** to maintain margin, or compete on enrichment + monitoring differentiators rather than raw price.
- Re-measure with `apify run` after any change before locking the production price.

The pay-per-event configuration lives in `.actor/pay_per_event.json`. Final pricing is set in **Apify Console → Publication → Monetization**.

***

### Realistic expectations & limits

Indeed is **Cloudflare-protected** and aggressively rate-limits deep pagination. This actor uses a fingerprinted headless browser over Apify residential proxies — the most reliable self-contained approach. Plan accordingly:

- **Dependable yield** — ~1 results page per search (~15–25 jobs) plus full job details. Deeper pagination is attempted, but treat **page 1 as the reliable floor**.
- **Best fits** — scheduled monitoring (only-new-since-last-run), niche / targeted queries, salary and skill intelligence.
- **Less suited for** — bulk multi-thousand-job harvests in a single run. For that, run multiple narrower queries on a schedule.

***

### How it works

A single **`PlaywrightCrawler`** with:

- **Browser fingerprint injection** to look like real users.
- **Apify residential proxies** with session rotation per blocked request.
- **Cloudflare challenge detection** — interstitials (title, page markers, status codes) are detected and the session is retired and retried on a fresh fingerprint.
- **Bandwidth control** — images, fonts, media, and trackers are blocked at the network layer.

#### Parsing strategy

- **Listing pages** — extracts `window.mosaic.providerData["mosaic-provider-jobcards"]` JSON via a brace-matching extractor, with explicit "no results" vs "soft block" detection.
- **Detail pages** — deep-searches `window._initialData` for the job model (resilient to Indeed reshaping JSON paths), with `<title>` + regex fallbacks.
- **Pagination** — `start` parameter is stepped by Indeed's real page size (15). Stops at the last (short) page instead of hammering a dead offset and triggering blocks.

If Indeed changes its private remote/hybrid filter codes, the actor falls back to a query keyword so runs don't fail outright.

***

### Local development

```bash
git clone <this-repo>
cd indeed-jobs-scraper
npm install

## Edit your test input:
##   storage/key_value_stores/default/INPUT.json

npx apify run          ## or: npm start
```

Requirements: **Node.js 20+**, **Docker** (for Apify build), and an Apify account if you want to deploy.

***

### FAQ

#### Is this Indeed scraper legal?

The actor scrapes only **publicly accessible** Indeed job postings. You are responsible for using the data in compliance with applicable laws (GDPR, CCPA, etc.) and Indeed's terms of service. This actor is not affiliated with Indeed.

#### Why are residential proxies required?

Indeed is protected by Cloudflare and blocks datacenter IPs almost instantly. The default Apify residential proxy group is strongly recommended — without it, runs will fail with Cloudflare challenges.

#### Can I scrape Indeed UK, Canada, Germany, or India?

Yes. Set the `country` field to any of 50+ supported domains (`gb`, `ca`, `de`, `in`, `fr`, `au`, etc.), or paste country-specific Indeed URLs directly into `urls`.

#### How do I get only new jobs since my last run?

Enable `"onlyNewSinceLastRun": true` and set a `dedupeStoreName`. Pair with a schedule and a `notifyWebhookUrl` for a fully automated **Indeed job alert** in Slack, Make, Zapier, or your own system.

#### Does it return salary data?

Yes — both **raw salary text** (`salary.salaryText`) and **normalized annualized salaries** (`normalizedSalary.annualMin/annualMax`) converted to your chosen `targetCurrency`. Hourly, weekly, and monthly rates are auto-annualized.

#### Can it extract skills from job descriptions?

Yes — enable `extractSkills` (default `true`). The actor parses the title + description into a `skills[]` array, plus detected `seniority` and `minYearsExperience`.

#### How fast is the actor?

With defaults (`maxConcurrency: 5`, full job details enabled), expect **~3–6 seconds per job** end-to-end (browser load + Cloudflare wait + detail parse). Disable `scrapeJobDetails` for a 5× faster listing-only run.

#### What if Indeed changes its HTML?

Parsing reads embedded JSON (`window.mosaic.providerData`, `window._initialData`) rather than DOM selectors, which is far more resilient to HTML reshuffles. Where regex fallbacks exist, they are documented in `src/parsers/`.

#### Can I get the full HTML description?

Yes — set `"includeDescriptionHtml": true` to receive `descriptionHtml` alongside `descriptionText`.

***

### Related Apify scrapers

- **LinkedIn Jobs Scraper** — extract jobs from LinkedIn
- **Glassdoor Jobs Scraper** — jobs + company reviews + salary data
- **ATS Jobs Scraper** — direct from Greenhouse, Lever, Ashby, Workday career pages
- **Job Posting Aggregator** — combine Indeed + LinkedIn + Glassdoor into one feed

***

### Support

Open an issue or contact the maintainer via the Apify Store actor page. Bugs reproduced with a public input JSON + a non-blocked URL are the fastest to resolve.

**Tags:** indeed scraper, indeed jobs scraper, scrape indeed, indeed api, job scraper, jobs api, salary data, recruitment data, hiring intelligence, job board scraper, indeed.com scraper, job listings scraper, ATS feed, job aggregator, indeed UK, indeed Canada, indeed India.

# Actor input Schema

## `urls` (type: `array`):

Indeed search or company job URLs to scrape. When set, the query-based parameters below are ignored. Example: https://www.indeed.com/jobs?q=developer or https://www.indeed.com/cmp/Google/jobs

## `maxRowsPerUrl` (type: `integer`):

Maximum number of jobs to scrape for each URL in the list above. 0 = no limit.

## `query` (type: `string`):

Job keywords, e.g. "data analyst". Required when not using URLs.

## `country` (type: `string`):

Indeed country domain to search.

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

City, state, or postcode, e.g. "San Francisco, CA".

## `radius` (type: `string`):

Distance from the location to include.

## `jobType` (type: `string`):

Filter by employment type.

## `level` (type: `string`):

Filter by required experience level.

## `sort` (type: `string`):

Result ordering on Indeed.

## `fromDays` (type: `string`):

Only include jobs posted within this many days.

## `remote` (type: `string`):

Restrict to remote or hybrid roles.

## `maxRows` (type: `integer`):

Maximum total jobs to scrape across all search pages. 0 = no limit.

## `includeSimilarJobs` (type: `boolean`):

Also collect Indeed's 'similar to your search' suggestions.

## `scrapeJobDetails` (type: `boolean`):

Visit each job's detail page for the full description, requirements, benefits and company profile. Disable for a faster, lighter, listing-only run.

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

Include the raw HTML job description in addition to plain text.

## `extractSkills` (type: `boolean`):

Parse the description into structured skills\[], detected seniority and years-of-experience.

## `normalizeSalary` (type: `boolean`):

Add normalizedSalary{} with annualized min/max converted to a single target currency.

## `targetCurrency` (type: `string`):

Currency to convert normalized salaries into.

## `fxRatesOverride` (type: `object`):

Optional. Override built-in exchange rates, expressed as units of currency per 1 USD. Example: {"EUR": 0.92, "GBP": 0.79}

## `deduplicate` (type: `boolean`):

Drop duplicate jobKeys seen during this run.

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

Cross-run deduplication: persist seen jobKeys in a named store and only output jobs not seen in previous runs. Ideal for scheduled monitoring.

## `dedupeStoreName` (type: `string`):

Named key-value store used to persist seen jobKeys across runs.

## `notifyWebhookUrl` (type: `string`):

Optional. When set, the run POSTs a JSON summary of the jobs it produced to this URL on finish. Paired with 'Only NEW jobs since last run' + a schedule, this delivers only new postings to Slack / Make / Zapier / your endpoint. Failures never fail the scrape.

## `maxConcurrency` (type: `integer`):

Maximum parallel browser pages. Lower values are gentler on anti-bot systems.

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

Residential proxies are strongly recommended — Indeed blocks datacenter IPs.

## Actor input object example

```json
{
  "urls": [],
  "maxRowsPerUrl": 100,
  "query": "data analyst",
  "country": "us",
  "location": "New York, NY",
  "sort": "relevance",
  "maxRows": 100,
  "includeSimilarJobs": true,
  "scrapeJobDetails": true,
  "includeDescriptionHtml": false,
  "extractSkills": true,
  "normalizeSalary": true,
  "targetCurrency": "USD",
  "fxRatesOverride": {},
  "deduplicate": true,
  "onlyNewSinceLastRun": false,
  "dedupeStoreName": "indeed-seen-jobs",
  "maxConcurrency": 5,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

All scraped job records (default dataset).

## `datasetUrl` (type: `string`):

Base API URL of this run's default dataset. Append ?format=csv / ?clean=true yourself when calling the API.

# 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 = {
    "urls": [],
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("mukeshrana90/indeed-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 = {
    "urls": [],
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("mukeshrana90/indeed-jobs-scraper").call(run_input=run_input)

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

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

```

## CLI example

```bash
echo '{
  "urls": [],
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call mukeshrana90/indeed-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=mukeshrana90/indeed-jobs-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Indeed Jobs Scraper",
        "description": "Extract detailed job listings from Indeed by search query + filters or by URL. Returns ~60 structured fields per job plus cross-run deduplication, skill/seniority extraction, and normalized salaries.",
        "version": "1.0",
        "x-build-id": "BmWZuJic4VjgBvXZ1"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/mukeshrana90~indeed-jobs-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-mukeshrana90-indeed-jobs-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/mukeshrana90~indeed-jobs-scraper/runs": {
            "post": {
                "operationId": "runs-sync-mukeshrana90-indeed-jobs-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/mukeshrana90~indeed-jobs-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-mukeshrana90-indeed-jobs-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "urls": {
                        "title": "Indeed URLs",
                        "type": "array",
                        "description": "Indeed search or company job URLs to scrape. When set, the query-based parameters below are ignored. Example: https://www.indeed.com/jobs?q=developer or https://www.indeed.com/cmp/Google/jobs",
                        "items": {
                            "type": "string"
                        }
                    },
                    "maxRowsPerUrl": {
                        "title": "Max results per URL",
                        "minimum": 0,
                        "type": "integer",
                        "description": "Maximum number of jobs to scrape for each URL in the list above. 0 = no limit.",
                        "default": 100
                    },
                    "query": {
                        "title": "Search query",
                        "type": "string",
                        "description": "Job keywords, e.g. \"data analyst\". Required when not using URLs."
                    },
                    "country": {
                        "title": "Country",
                        "enum": [
                            "us",
                            "gb",
                            "ca",
                            "au",
                            "in",
                            "ie",
                            "nz",
                            "sg",
                            "za",
                            "de",
                            "fr",
                            "es",
                            "it",
                            "nl",
                            "be",
                            "ch",
                            "at",
                            "se",
                            "no",
                            "dk",
                            "fi",
                            "pl",
                            "pt",
                            "cz",
                            "ro",
                            "gr",
                            "hu",
                            "br",
                            "mx",
                            "ar",
                            "cl",
                            "co",
                            "pe",
                            "ae",
                            "sa",
                            "qa",
                            "kw",
                            "il",
                            "tr",
                            "eg",
                            "ng",
                            "ke",
                            "jp",
                            "kr",
                            "hk",
                            "tw",
                            "my",
                            "ph",
                            "id",
                            "th",
                            "vn",
                            "pk",
                            "bd"
                        ],
                        "type": "string",
                        "description": "Indeed country domain to search.",
                        "default": "us"
                    },
                    "location": {
                        "title": "Location",
                        "type": "string",
                        "description": "City, state, or postcode, e.g. \"San Francisco, CA\"."
                    },
                    "radius": {
                        "title": "Search radius (miles)",
                        "enum": [
                            "0",
                            "5",
                            "10",
                            "15",
                            "25",
                            "35",
                            "50",
                            "100"
                        ],
                        "type": "string",
                        "description": "Distance from the location to include."
                    },
                    "jobType": {
                        "title": "Job type",
                        "enum": [
                            "fulltime",
                            "parttime",
                            "contract",
                            "internship",
                            "temporary",
                            "permanent",
                            "seasonal",
                            "freelance"
                        ],
                        "type": "string",
                        "description": "Filter by employment type."
                    },
                    "level": {
                        "title": "Experience level",
                        "enum": [
                            "entry_level",
                            "mid_level",
                            "senior_level"
                        ],
                        "type": "string",
                        "description": "Filter by required experience level."
                    },
                    "sort": {
                        "title": "Sort by",
                        "enum": [
                            "relevance",
                            "date"
                        ],
                        "type": "string",
                        "description": "Result ordering on Indeed.",
                        "default": "relevance"
                    },
                    "fromDays": {
                        "title": "Posted within (days)",
                        "enum": [
                            "1",
                            "3",
                            "7",
                            "14"
                        ],
                        "type": "string",
                        "description": "Only include jobs posted within this many days."
                    },
                    "remote": {
                        "title": "Remote / hybrid",
                        "enum": [
                            "remote",
                            "hybrid"
                        ],
                        "type": "string",
                        "description": "Restrict to remote or hybrid roles."
                    },
                    "maxRows": {
                        "title": "Max results",
                        "minimum": 0,
                        "type": "integer",
                        "description": "Maximum total jobs to scrape across all search pages. 0 = no limit.",
                        "default": 100
                    },
                    "includeSimilarJobs": {
                        "title": "Include similar jobs",
                        "type": "boolean",
                        "description": "Also collect Indeed's 'similar to your search' suggestions.",
                        "default": true
                    },
                    "scrapeJobDetails": {
                        "title": "Scrape full job details",
                        "type": "boolean",
                        "description": "Visit each job's detail page for the full description, requirements, benefits and company profile. Disable for a faster, lighter, listing-only run.",
                        "default": true
                    },
                    "includeDescriptionHtml": {
                        "title": "Include HTML description",
                        "type": "boolean",
                        "description": "Include the raw HTML job description in addition to plain text.",
                        "default": false
                    },
                    "extractSkills": {
                        "title": "Extract skills & seniority",
                        "type": "boolean",
                        "description": "Parse the description into structured skills[], detected seniority and years-of-experience.",
                        "default": true
                    },
                    "normalizeSalary": {
                        "title": "Normalize salary",
                        "type": "boolean",
                        "description": "Add normalizedSalary{} with annualized min/max converted to a single target currency.",
                        "default": true
                    },
                    "targetCurrency": {
                        "title": "Target currency",
                        "enum": [
                            "USD",
                            "EUR",
                            "GBP",
                            "CAD",
                            "AUD",
                            "INR",
                            "SGD",
                            "ZAR",
                            "BRL",
                            "MXN",
                            "AED",
                            "JPY",
                            "CHF",
                            "SEK",
                            "PLN"
                        ],
                        "type": "string",
                        "description": "Currency to convert normalized salaries into.",
                        "default": "USD"
                    },
                    "fxRatesOverride": {
                        "title": "FX rates override",
                        "type": "object",
                        "description": "Optional. Override built-in exchange rates, expressed as units of currency per 1 USD. Example: {\"EUR\": 0.92, \"GBP\": 0.79}",
                        "default": {}
                    },
                    "deduplicate": {
                        "title": "Deduplicate within run",
                        "type": "boolean",
                        "description": "Drop duplicate jobKeys seen during this run.",
                        "default": true
                    },
                    "onlyNewSinceLastRun": {
                        "title": "Only NEW jobs since last run",
                        "type": "boolean",
                        "description": "Cross-run deduplication: persist seen jobKeys in a named store and only output jobs not seen in previous runs. Ideal for scheduled monitoring.",
                        "default": false
                    },
                    "dedupeStoreName": {
                        "title": "Dedupe store name",
                        "type": "string",
                        "description": "Named key-value store used to persist seen jobKeys across runs.",
                        "default": "indeed-seen-jobs"
                    },
                    "notifyWebhookUrl": {
                        "title": "Notify webhook URL",
                        "type": "string",
                        "description": "Optional. When set, the run POSTs a JSON summary of the jobs it produced to this URL on finish. Paired with 'Only NEW jobs since last run' + a schedule, this delivers only new postings to Slack / Make / Zapier / your endpoint. Failures never fail the scrape."
                    },
                    "maxConcurrency": {
                        "title": "Max concurrency",
                        "minimum": 1,
                        "maximum": 25,
                        "type": "integer",
                        "description": "Maximum parallel browser pages. Lower values are gentler on anti-bot systems.",
                        "default": 5
                    },
                    "proxyConfiguration": {
                        "title": "Proxy configuration",
                        "type": "object",
                        "description": "Residential proxies are strongly recommended — Indeed blocks datacenter IPs.",
                        "default": {
                            "useApifyProxy": true,
                            "apifyProxyGroups": [
                                "RESIDENTIAL"
                            ]
                        }
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
