# Indeed Jobs Scraper - Salary & Company Data ($0.25/1k) (`harshmaur/indeed-scraper`) Actor

Scrape Indeed job listings by keyword, location, or any search URL across 60+ country sites - no API key, no login. Get salary (parsed min/max), company profile, hiring signals, and the full description as text, Markdown & HTML. Pay only per result. Works with AI Agents, MCP, n8n, Make & Zapier.

- **URL**: https://apify.com/harshmaur/indeed-scraper.md
- **Developed by:** [Harsh Maur](https://apify.com/harshmaur) (community)
- **Categories:** Jobs, AI, Agents
- **Stats:** 9 total users, 2 monthly users, 96.2% runs succeeded, 2 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $0.20 / 1,000 job listings

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?

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

## Indeed Jobs Scraper — Scrape Indeed Jobs, Salaries & Company Data in 60+ Countries (No API Key)

Pull job listings from **Indeed** at scale — by job title and location on any country's Indeed site, or directly from Indeed search URLs. Each job comes back with the company, salary, location, posting date, hiring signals, company profile, and the full description — exported to JSON, CSV, or Excel, or via the Apify API. **No Indeed API key, login, or cookies needed.**

### What does Indeed Jobs Scraper do?

It converts an Indeed search into a structured dataset — one row per job, consistent shape, ready for analysis or automation.

#### ✅ No API key or account

Just a job title and location (or a search URL). Nothing to sign up for.

#### 🌍 60+ country sites

Search US, UK, Canada, Germany, India, Australia, and dozens more — one input field switches the Indeed domain.

#### 💵 Salary, parsed

Compensation as a display string plus numeric `salaryMin`/`salaryMax` with currency and pay period — including Indeed's estimated ranges, flagged as estimates.

#### 🏢 Company profile included

Industry, revenue band, employee band, CEO, website, logo, and a short company blurb — alongside the job.

#### 🚩 Hiring signals

Flags for urgent hiring and high-volume hiring, plus a remote indicator.

#### 📝 Full descriptions inline

The complete job description (text + HTML) ships with every listing — no extra per-job requests.

#### 🔄 Export anywhere

JSON, CSV, Excel, HTML table, RSS, or JSONL — plus the Apify API and integrations.

### How to scrape jobs from Indeed

Enter a **job title**, a **location**, and a **country**, then run. The scraper pulls every matching Indeed listing, paginating automatically up to Indeed's ~1,000-result-per-search ceiling. Prefer to build the search on Indeed itself? Paste one or more **Indeed search URLs** and they're scraped as-is. Use the keyword fields, the URLs, or both — results combine and de-duplicate.

### What data can you extract from Indeed?

Every job is one dataset item (fields Indeed omits come back as `null`):

| Field | Description |
| --- | --- |
| `title`, `normalizedTitle` | Listing title + Indeed's normalized title |
| `company`, `companyUrl`, `companyRating`, `companyLogo` | Employer name, Indeed company page, star rating, logo |
| `companyIndustry`, `companyRevenue`, `companyEmployeeCount`, `companyCeo`, `companyWebsite`, `companyDescription` | Company profile (dossier) |
| `location`, `city`, `state`, `country`, `postalCode`, `streetAddress`, `latitude`, `longitude` | Parsed location, street address + coordinates |
| `isRemote` | Remote indicator |
| `salary`, `salaryMin`, `salaryMax`, `salaryType`, `salaryCurrency`, `salaryIsEstimated` | Compensation (real or Indeed-estimated) |
| `jobType`, `occupations`, `attributes` | Employment type, occupation tags, skills/benefits |
| `isUrgentHire`, `isHighVolumeHiring`, `expired` | Hiring signals + expiry |
| `postedAt` | ISO posting date |
| `descriptionText`, `descriptionMarkdown`, `descriptionHtml` | Full description as clean text, **Markdown**, and HTML |
| `url`, `jobKey`, `refNum` | Apply/view URL + Indeed keys |
| `searchTerm`, `searchPosition`, `scrapedAt` | Search context + timestamp |

### Indeed job scraping use cases

- **Labor-market & salary analytics** — chart pay ranges and demand by role, region, and country.
- **Recruiting pipelines** — bulk-source live openings with employer context and apply links.
- **Lead generation** — find companies actively hiring, enriched with industry, size, and website.
- **Job boards & aggregators** — keep a vertical board fresh with structured Indeed listings.
- **Competitive & hiring intelligence** — watch competitors' openings, locations, and urgency signals.
- **AI & RAG pipelines** — feed clean descriptions into matching, tagging, or summarization.

### How to scrape Indeed jobs by title and location

#### All jobs for a role in a city

```json
{ "position": "software engineer", "location": "New York, NY", "country": "us", "maxItems": 500 }
```

#### Remote jobs across a whole country

```json
{ "position": "customer support", "location": "Remote", "country": "us", "maxItems": 200 }
```

#### Search another country's Indeed site

```json
{ "position": "data analyst", "location": "London", "country": "uk", "datePosted": "7" }
```

#### Only the freshest postings (last 24 hours)

```json
{ "position": "registered nurse", "location": "Chicago, IL", "datePosted": "1" }
```

### How to scrape Indeed jobs from a search URL

```json
{ "startUrls": [{ "url": "https://www.indeed.com/jobs?q=python+developer&l=Austin%2C+TX" }], "maxItems": 300 }
```

### Fast, lightweight runs

Skip the full description for a leaner, cheaper run that still returns title, company, salary, location, rating, and posting date:

```json
{ "position": "warehouse associate", "location": "Dallas, TX", "scrapeDescription": false, "maxItems": 0 }
```

### Limiting results

- **Max jobs** (`maxItems`) caps the total across all searches + URLs; `0` = no cap.
- A single search tops out at Indeed's ~1,000-result ceiling — combine several keywords, locations, or URLs to gather more.

### Output example

```json
{
  "dataType": "job",
  "jobKey": "9ded925f7e009b39",
  "title": "Software Development Engineer II - Network Performance Monitoring",
  "normalizedTitle": "software engineer",
  "url": "https://www.amazon.jobs/jobs/10444205/...",
  "company": "Amazon.com",
  "companyUrl": "https://www.indeed.com/cmp/Amazon.com",
  "companyRating": null,
  "companyLogo": "https://d2q79iu7y748jz.cloudfront.net/s/_squarelogo/256x256/...",
  "companyWebsite": "https://www.amazon.jobs/",
  "companyIndustry": "Internet and Software",
  "companyRevenue": "more than $10B (USD)",
  "companyEmployeeCount": "10,000+",
  "companyCeo": "Andrew Jassy",
  "companyDescription": "Our mission is to be Earth's most customer-centric company.",
  "location": "Santa Clara, CA",
  "city": "Santa Clara",
  "state": "CA",
  "country": "US",
  "isRemote": false,
  "salary": "$165,200 - $223,600 a year",
  "salaryMin": 165200,
  "salaryMax": 223600,
  "salaryType": "YEAR",
  "salaryCurrency": "USD",
  "salaryIsEstimated": false,
  "jobType": ["Full-time"],
  "occupations": ["Software Development Occupations"],
  "attributes": ["AWS", "Distributed computing", "401(k) matching"],
  "isUrgentHire": false,
  "isHighVolumeHiring": true,
  "expired": false,
  "refNum": "10444205",
  "postedAt": "2026-06-08T05:00:00.000Z",
  "descriptionText": "DESCRIPTION\n\nAWS Infrastructure Services owns the design…",
  "descriptionMarkdown": "## DESCRIPTION\n\nAWS Infrastructure Services owns the design…",
  "descriptionHtml": "<p>DESCRIPTION…</p>",
  "searchTerm": "software engineer",
  "searchPosition": 6,
  "scrapedAt": "2026-06-10T10:11:31.362Z"
}
```

Note: `companyRating` is exposed but Indeed's search API populates it inconsistently (often `null`).

### How much does it cost to scrape Indeed jobs?

Pay only for results — **no start fee, no monthly subscription**. From **$0.25 per 1,000 jobs**, billed per unique job returned (duplicates across overlapping pages are removed before charging). That's a fraction of the price of the popular Indeed scrapers — and you get more per job: parsed salary (min/max + currency), full company profile, hiring signals, and the complete description as text, Markdown & HTML. Turn **Scrape full job description** off for an even leaner, cheaper run.

### How the Indeed scraper works

It reads the same GraphQL API Indeed's mobile app uses (`apis.indeed.com/graphql`) through a proxy. That endpoint returns up to **100 fully-detailed jobs per request — with the complete description inline** — and paginates via cursor, so one search can yield thousands of jobs. (Indeed's public HTML pages cap at ~15 jobs and block deep pagination.) No headless browser is involved, so runs are fast and cheap: 250 jobs is just 3 requests.

### Use Indeed Jobs Scraper with AI agents and MCP

This Actor is callable by AI agents via the **Apify MCP server**, so assistants like Claude can run it and read results as a tool. It also slots into LLM workflows — route `descriptionText` into screening, tagging, or summarization prompts.

#### Push new jobs straight to Slack, Notion, or Sheets (no code)

The other direction: pick an **MCP connector** in the input (**Send results to an app**) and every scraped job is delivered into the app you authorized — Slack, Notion, Airtable, Google Sheets, and more. Your credentials stay in Apify; the actor never sees them.

The killer setup is a **new-job alert**: schedule this actor for your search with the **Last 24 hours** date filter and a Slack connector — you get pinged the moment a matching role is posted. Choose **one message per job** or a **single run summary**, and customize the message with `{{field}}` placeholders (`{{title}}`, `{{company}}`, `{{location}}`, `{{url}}`, …). Delivery is best-effort and never blocks or fails the scrape.

### Integrate with n8n, Zapier, and Make

**Run and get results synchronously (small scrapes):**

```
POST https://api.apify.com/v2/acts/harshmaur~indeed-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN
Content-Type: application/json

{ "position": "data analyst", "location": "Remote", "country": "us", "maxItems": 100 }
```

**Run asynchronously (large scrapes), then fetch the dataset when finished:**

```
POST https://api.apify.com/v2/acts/harshmaur~indeed-scraper/runs?token=YOUR_TOKEN
```

Schedule recurring runs from the Apify console for a continuously refreshed Indeed feed.

### Support

Spotted a bug or want an extra field? Open an issue on the Actor's **Issues** tab — feedback and feature requests are welcome.

### FAQ

#### Do I need an Indeed API key or login?

No. Provide a job title and location (or paste an Indeed search URL) and run — no account, cookies, or API key.

#### How many jobs can I scrape from Indeed?

A single search returns up to ~1,000 listings (Indeed's per-search ceiling) via automatic pagination; combine multiple keywords, locations, or Search URLs to pull more.

#### Which countries are supported?

60+ Indeed country sites — pick the **Country** (US, UK, Canada, Germany, India, Australia, and many more).

#### Can I scrape only remote jobs?

Yes — set **Location** to `Remote`, or paste an Indeed remote-filtered search URL.

#### Does every job include salary?

When Indeed lists or estimates pay, you get the display string plus numeric min/max, currency, and period; `salaryIsEstimated` flags Indeed's estimates. Listings without pay return `null` salary fields.

#### Does it include the full job description?

Yes — every listing's description is returned as clean text, **Markdown** (`descriptionMarkdown`), and raw HTML. Turn `scrapeDescription` off for a faster, lighter run with the core fields only.

#### Which export formats are supported?

JSON, CSV, Excel, HTML table, RSS, and JSONL, plus the Apify API.

#### Can AI agents use this scraper?

Yes — it's available as a tool via the Apify MCP server.

### Other Actors

Part of a family of no-login, pay-per-result scrapers:

| Actor | Description |
| --- | --- |
| [LinkedIn Jobs Scraper](https://apify.com/harshmaur/linkedin-jobs-scraper) | LinkedIn listings with salary, recruiter contact, and company details |
| [Reddit Scraper](https://apify.com/harshmaur/reddit-scraper) | Posts, comments, communities, and user profiles |
| [Reddit Comments Scraper](https://apify.com/harshmaur/reddit-comments-scraper) | Every comment and nested reply from any post |
| [Reddit Search Scraper](https://apify.com/harshmaur/reddit-search-scraper) | Keyword search across posts, comments, and communities |
| [Reddit Subreddit Scraper](https://apify.com/harshmaur/reddit-subreddit-scraper) | Entire subreddits, past the listing cap |

Run the **Indeed** and **LinkedIn** scrapers together to build a cross-source jobs dataset.

### Resources

- 📖 [Apify Platform Documentation](https://docs.apify.com/)
- 📝 [Web Scraping Best Practices](https://blog.apify.com/web-scraping-best-practices/)
- ⚖️ [Is Web Scraping Legal?](https://blog.apify.com/is-web-scraping-legal/)

# Actor input Schema

## `position` (type: `string`):

What to search for, e.g. "software engineer", "registered nurse", "python developer". Leave empty if you are only using Search URLs below.

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

City, state, ZIP, or "remote" — e.g. "New York, NY", "London", "94103". Leave empty to search the whole country.

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

Indeed country site to search. Determines the domain (e.g. US → indeed.com, UK → uk.indeed.com).

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

Only return jobs posted within this window.

## `startUrls` (type: `array`):

Paste one or more Indeed search/result URLs to scrape directly (e.g. a filtered search you built on indeed.com). The keyword fields above do NOT apply to these — they are scraped as-is, then paginated.

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

Maximum number of job listings to scrape across all searches and URLs. Indeed caps a single search at ~1000 results. Set 0 for no limit (stops when Indeed runs out of results).

## `scrapeDescription` (type: `boolean`):

Fetch the complete job description text and HTML for every listing. Adds a small number of extra requests (descriptions are fetched in batches). Disable for a faster, lighter run that still returns title, company, salary, location, rating, and posting date.

## `mcpConnector` (type: `string`):

Pick an authorized MCP connector (Slack, Notion, Airtable, Google Sheets, …) to receive the scraped jobs. This is the recommended, secure path — your credentials stay in Apify and are never exposed to this actor (Apify's proxy injects them). Leave empty to scrape only. Tip: schedule this actor with the 'Last 24 hours' date filter to get pinged whenever a matching job is posted.

## `mcpMode` (type: `string`):

perPost: send one message/record per scraped job. summary: send a single digest of the whole run.

## `mcpTarget` (type: `string`):

Where to write — the destination ID the tool needs: a Notion database/page ID, a Google Sheet ID, or a Slack channel. Leave blank for connectors that post to a default location. Advanced shapes go in Arguments JSON below.

## `mcpMessage` (type: `string`):

Template for each message. Use {{field}} placeholders from ANY job field — common ones: {{title}}, {{company}}, {{location}}, {{url}}, {{postedAt}}, {{salary}}, {{isRemote}}, {{jobType}}, {{companyRating}}. (Full field list in the README.) Missing fields render empty.

## `mcpTool` (type: `string`):

Force a specific tool name on the connector (e.g. send\_message, create\_page, append\_row). Leave empty to auto-detect the connector's write/notify tool.

## `mcpArguments` (type: `object`):

Extra/override arguments merged into each tool call as JSON — e.g. {"channel": "#jobs"} for Slack. {{field}} placeholders work inside string values too, so you can nest live data into any shape the tool needs — e.g. Notion: {"properties": {"Title": {"title": \[{"text": {"content": "{{title}}"}}]}, "Link": {"url": "{{url}}"}}}. Use it to satisfy required fields or fully control the payload.

## `mcpMaxItems` (type: `integer`):

Safety cap on how many jobs are piped to the connector per run (perPost mode). Protects against flooding the destination.

## `mcpServerUrl` (type: `string`):

Advanced/testing only. Connect directly to a raw MCP server URL, bypassing the MCP Connector above. For real use prefer a connector — with a connector your credentials stay in Apify and are never seen by this actor. When set, this overrides the connector.

## `mcpServerToken` (type: `string`):

Bearer token for the dev MCP server URL above. Note: a token entered here IS visible to the actor at runtime — for production use an MCP Connector instead, so credentials never leave Apify. Ignored unless the URL is set.

## Actor input object example

```json
{
  "position": "software engineer",
  "location": "New York, NY",
  "country": "us",
  "datePosted": "",
  "startUrls": [],
  "maxItems": 50,
  "scrapeDescription": true,
  "mcpMode": "perPost",
  "mcpMessage": "*{{title}}* — {{company}}\n{{location}}\n{{url}}",
  "mcpMaxItems": 50
}
```

# Actor output Schema

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

All scraped Indeed job listings.

# 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 = {
    "position": "software engineer",
    "location": "New York, NY",
    "country": "us",
    "datePosted": "",
    "maxItems": 50,
    "mcpMode": "perPost",
    "mcpMessage": `*{{title}}* — {{company}}
{{location}}
{{url}}`,
    "mcpMaxItems": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("harshmaur/indeed-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 = {
    "position": "software engineer",
    "location": "New York, NY",
    "country": "us",
    "datePosted": "",
    "maxItems": 50,
    "mcpMode": "perPost",
    "mcpMessage": """*{{title}}* — {{company}}
{{location}}
{{url}}""",
    "mcpMaxItems": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("harshmaur/indeed-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 '{
  "position": "software engineer",
  "location": "New York, NY",
  "country": "us",
  "datePosted": "",
  "maxItems": 50,
  "mcpMode": "perPost",
  "mcpMessage": "*{{title}}* — {{company}}\\n{{location}}\\n{{url}}",
  "mcpMaxItems": 50
}' |
apify call harshmaur/indeed-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,harshmaur/indeed-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/5SUiq2I7K9yPP0J1K/builds/T5IjHTo54fFBqAjbD/openapi.json
