# Indeed Job Scraper (`boncross/indeed-jobs`) Actor

Scrape Indeed job listings for a role and location. Get the full job description, salary range with its source, company name, industry and size, remote and job-type flags, and the posting date. Export to CSV, JSON or Excel, or schedule it.

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

## Pricing

from $0.06 / 1,000 job-listeds

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

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

## What's an Apify Actor?

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

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

## How to integrate an Actor?

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

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

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

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

# README

## Indeed Job Scraper

### What is Indeed Job Scraper?

Indeed Job Scraper gets you Indeed listings with the whole advert on each row for $0.08 per 1,000 jobs. That's the Free plan price, and it's all you pay. There's no second step or second charge to fetch descriptions: the text arrives with the listing.

Enter a job title and a place, pick one of 16 national Indeed sites, and run it. Four London searches returned 1,642 jobs in 12 to 18 seconds.

Three more things you'd want to know when comparing:

- **You can tell a stated salary from a guess.** Each pay figure is marked as the employer's own or Indeed's estimate, and you can ask for employer-stated pay only.
- **A missing tag is never turned into "no".** The full-time, part-time, contract and internship flags are empty when Indeed has no job-type tag for the job.
- **Two links per job.** One is Indeed's page for the job. The other is the employer's application page, where Indeed links out.

**BonCross Verified** means we tested this actor against the live site before release and kept the record:

- 22 of 22 live checks on its inputs and filters passed on 7 October 2026
- 5 markets run the same day, the US, Germany, Japan, France and India: 165 jobs, none empty

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

| Event | Free plan | Starter | Scale | Business |
|---|---|---|---|---|
| `actor-start`, per run | $0.001 | $0.001 | $0.001 | $0.001 |
| `search-run`, per search the run starts | $0 | $0 | $0 | $0 |
| `job-listed`, per job row written | $0.00008 | $0.000072 | $0.000064 | $0.00006 |

Enterprise plans pay the Business price.

**$0.08 per 1,000 jobs** on the Free plan, $0.06 on Business. Proxy usage and platform usage are included, and the price is the same with the advert text on or off.

Four searches in London returning 1,642 jobs: $0.001 + 1,642 × $0.00008 = **$0.13236** ($0.0995 on Business).

You aren't charged for a job that's filtered out before it's written, or for an error row.

At 200 jobs a run, the $5 of monthly credits on Apify's Free plan covers about 290 runs. That's about 59,000 jobs.

### What data can you extract from Indeed?

| Field | Field |
|---|---|
| 💼 Job title | 🏢 Employer |
| 📄 Full advert text | Advert HTML, optional |
| 📍 Location as advertised | City, region and postcode |
| 🌍 Country and country code | 🔗 Apply link |
| 💷 Salary from and to | Salary period and currency |
| Whether the employer stated the salary | Job attributes and benefits |
| 🏠 Remote | Full-time |
| Part-time | Contract |
| Internship | Expired |
| 📅 Posted date | First listed date |
| Employer industry | Employer size |
| Employer logo | When the row was collected |

### How to scrape Indeed jobs

1. [Sign up for Apify](https://console.apify.com/sign-up). The free plan is enough to try this.
2. Open [Indeed Job Scraper](https://apify.com/boncross/indeed-jobs).
3. Type a job title or keyword and a location, then choose the Indeed country.
4. Set how many jobs you want per search, and for the run if you'd like a ceiling.
5. Press Start. A 25-job search took 4 seconds.
6. Download the results as Excel, CSV or JSON.

### ⬇️ Input

A keyword, a location, or both. The country defaults to the United Kingdom.

"Max rows per search" (default 200) caps each search. Set it to 0 to take everything Indeed offers, which is roughly 1,000 a search. "Max rows in total" (default 0, no ceiling) caps the run.

"More searches" takes extra keywords that share your location. Through the API, an entry can be an object with a `keyword` and `location` of its own, as in the third example.

**Paralegal jobs in London**

```json
{
  "keyword": "paralegal",
  "location": "London",
  "country": "GB",
  "maxItemsPerSearch": 200
}
```

**Remote roles paying £40,000 or more, employer-stated salaries only**

```json
{
  "keyword": "software engineer",
  "country": "GB",
  "onlyRemote": true,
  "onlyEmployerStatedPay": true,
  "minPay": 40000
}
```

**Several searches in different cities**

```json
{
  "searches": [
    { "keyword": "paralegal", "location": "London" },
    { "keyword": "legal secretary", "location": "Manchester" }
  ],
  "country": "GB",
  "maxItems": 500
}
```

**A smaller dataset with no advert text**

```json
{
  "keyword": "data analyst",
  "location": "New York",
  "country": "US",
  "includeDescription": false
}
```

The remaining settings are optional:

- **Search area.** "Search radius" (default 25) and its unit, miles or kilometres (miles by default).
- **Filters.** "Exclude expired listings" is on by default. "Only remote jobs", "Only employer-stated pay" and "Minimum pay" narrow further.
- **What each row includes.** "Include job description" (on by default) is the advert as plain text. "Include description HTML" (off by default) adds the advert with its formatting and makes rows a good deal bigger.

#### For developers

- Keys: `keyword`, `location`, `country`, `searches`, `maxItemsPerSearch`, `maxItems`, `radius`, `radiusUnit` (`MILES` or `KILOMETERS`), `excludeExpired`, `onlyRemote`, `onlyEmployerStatedPay`, `minPay`, `includeDescription`, `includeDescriptionHtml`, `maxConcurrency`, `delayMs`.
- Indeed returns up to 100 jobs a page and stops at roughly 1,000 per search.
- When your filters keep nothing (a pay floor no advert reaches, say), a search stops early: after 5 pages in a row with no job kept, or after about three times the pages the remaining row limit needs.
- `maxConcurrency` (default 4, from 1 to 32) is the number of parallel requests. `delayMs` (default 0, up to 500) pauses before each one.
- With both description outputs off, the advert text isn't downloaded at all, which is why those runs are several times faster.
- If Apify moves a run to another server part-way through, it resumes where it stopped. You won't get or pay for a job twice.

### ⬆️ Output

You get one row per job. The Overview view shows title, employer, location, salary and dates. The full record has the advert text, the structured address and the job attributes. If you switched on the HTML option, the advert is in `descriptionHtml` as well.

```json
{
  "id": "0c432d8a6def9e48",
  "title": "Costs Draftsperson",
  "url": "https://www.indeed.com/viewjob?jk=0c432d8a6def9e48",
  "applyUrl": "https://thompsonslaw.pinpointhq.com/postings/…",
  "company": "Thompsons Solicitors",
  "companyUrl": "https://www.indeed.com/cmp/Thompsons-Solicitors",
  "companyIndustry": null,
  "companyIndustryCode": null,
  "companySize": "201 to 500",
  "companyLogo": "https://d2q79iu7y748jz.cloudfront.net/s/_squarelogo/256x256/…",
  "location": "London EC2R 8DN",
  "city": "London",
  "region": "ENG",
  "postcode": "EC2R 8DN",
  "countryCode": "GB",
  "isExpired": false,
  "postedAt": "2026-09-25T13:12:00.000Z",
  "listedAt": "2026-09-25T13:15:09.561Z",
  "payMin": 41369,
  "payMax": 41369,
  "payPeriod": "YEAR",
  "payCurrency": "GBP",
  "paySource": "EMPLOYER_PROVIDED",
  "isRemote": null,
  "isFullTime": true,
  "isPartTime": false,
  "isContract": false,
  "isInternship": false,
  "attributes": [
    "Microsoft Word",
    "Additional leave",
    "Full-time",
    "Microsoft Office Suite"
  ],
  "description": "Description\n\nAre you an experienced Costs Draftsperson looki…",
  "scrapedAt": "2026-09-26T07:44:59.537Z",
  "search": "paralegal in London",
  "country": "GB"
}
```

| Column | Meaning |
|---|---|
| `url` | Indeed's own page for the job, so rows can be matched against Indeed |
| `applyUrl` | Where the employer takes applications, where Indeed links out |
| `companyUrl`, `companySize`, `companyIndustry`, `companyLogo` | The employer's Indeed profile, size band, industry and logo, where Indeed has them |
| `companyIndustryCode` | Indeed's own code for the industry, beside the readable name |
| `postedAt`, `listedAt` | When the employer published the job, and when it appeared on Indeed |
| `isExpired` | The job is closed |
| `isFullTime`, `isPartTime`, `isContract`, `isInternship` | From Indeed's job-type tags: empty when the job carries none, so a missing tag is never read as "no" |
| `isRemote` | True where Indeed tags the job remote, otherwise empty |
| `paySource` | `EMPLOYER_PROVIDED` for a stated salary, `ESTIMATED` for Indeed's own estimate |
| `search` | The search that found the job |

### How many Indeed jobs can you get, and how fast?

| Run | Jobs returned | Time |
|---|---:|---:|
| Paralegal in London, 25 jobs | 25 | 4 seconds |
| Three searches in London, up to 300 each | 741 | 7-97 seconds |
| Six searches across five cities, up to 200 each | 913 | 18-77 seconds |
| Sixteen searches, one page each | 1,340 | 18-56 seconds |
| Four searches in London, up to 500 each | 1,642 | 12-18 seconds |
| Three searches in London, up to 300 each, advert text off | 742 | 2-4 seconds |

Your searches run several at a time. Indeed's own response time swings a lot from hour to hour, which is why one run can take a few times longer at a busy moment. The ranges above show it.

On a 50-row run, the job key, title, links, location, city, region, country, both dates, the attributes, the advert text and the expired flag were on **every row**. The employer name was on 48, the job-type flags on 41 (the others had no job-type tag), salary on 22, company size on 26, postcode on 30 and employer industry on 11. All 22 salaries were employer-stated. With the advert text included, rows averaged 5 KB and adverts 3,588 characters.

### What Indeed limits

- **About 1,000 results per search.** Indeed stops issuing more. Narrow your search, or add searches.
- **No sorting.** Indeed accepts a sort setting and ignores it, so you won't find one in the form.
- **Salary only where there is one.** It's present when the employer published it or Indeed estimated it, and the row says which.
- **Employer industry and size are patchy.** They're Indeed's own enrichment and many employers lack them.
- **`isRemote` is true or empty, never false.**
- **`applyUrl` doesn't contain the job key.** It's the employer's page. `url` is always Indeed's.
- **A few adverts end in an ellipsis.** They arrive shortened in Indeed's own data.
- **A stray corrupted character is removed.** A small number of adverts contain a character the employer's system had already broken before Indeed stored it.
- **No applications.** The actor reads jobs. It can't apply for them.

### FAQ

#### Can I search outside the United Kingdom?

Set "Indeed country" to any of the 16 markets: GB, US, CA, IE, AU, NZ, DE, FR, NL, ES, IT, SE, PL, IN, SG or JP. Give a location in that country, in whatever form you'd type it into Indeed there.

#### How do I get more than 1,000 jobs for a role?

Break the search up. "Nurse" in ten cities is ten searches with ten ceilings. The `search` column on each row tells you which search found it.

#### Why is the salary empty on some jobs?

The employer didn't publish one and Indeed had no estimate. On our 50-row test, 22 jobs had a figure.

#### Why are there two links?

`url` is Indeed's page for the job, which lets you match rows against Indeed. `applyUrl` is where a candidate ends up when they apply.

#### Does turning the description off make it cheaper?

It makes the run faster and the dataset smaller. The number of jobs and the charges stay the same.

#### Do I need a proxy or an Indeed account?

You don't need an account, and the proxy usage is included in the price. There's nothing to set up.

#### Is scraping Indeed legal?

The actor reads job adverts that employers publish openly, without logging in. Collecting data that's published to everyone is generally lawful in the UK and the EU. Your use of it is your responsibility, UK GDPR included if a record identifies a person.

### Pipelines, API and agents

At this price people tend to pull whole markets: a schedule per city or per role, with a webhook delivering each dataset to a warehouse, or the Google Sheets and Airbyte integrations doing it for you. Salary benchmarks should filter on `paySource` to keep stated figures only.

From code, call Apify's run-sync-get-dataset-items endpoint for this actor and the rows come back in the response. `apify-client` is available for Python and JavaScript, and the API tab on the actor's page has the reference with your token filled in.

Connect an MCP-capable AI client to `https://mcp.apify.com` and the actor becomes a tool an agent can call, for example to collect this week's remote data analyst roles and compare the stated pay.

### More from BonCross

- [Lever Jobs Scraper](https://apify.com/boncross/lever-jobs): A Lever careers board for every open job.
- [LinkedIn Jobs Scraper](https://apify.com/boncross/linkedin-jobs-scraper): LinkedIn jobs from keywords and locations or search URLs, no login needed.
- [NHS Jobs Scraper](https://apify.com/boncross/nhs-jobs): NHS Jobs vacancies by keyword and location.
- [Reed.co.uk Jobs Scraper](https://apify.com/boncross/reed-jobs-scraper): Reed.co.uk jobs by keyword and location with Reed's own filters: salary, contract,…
- [UK Government & NHS Jobs Scraper](https://apify.com/boncross/uk-public-sector-jobs): NHS Jobs, Civil Service Jobs (every department) and Ministry of Justice vacancies in…
- [Workday Jobs Scraper](https://apify.com/boncross/workday-jobs): Every open job from a Workday careers board.

### Your feedback

Seen a job on Indeed that this didn't return, or a flag that looks wrong? Report it on the Issues tab in Apify Console with the search you ran. We look at every report.

# Changelog

This Actor's version history is a separate document: https://apify.com/boncross/indeed-jobs/changelog.md

# Actor input Schema

## `keyword` (type: `string`):

What to search for, e.g. paralegal or data analyst. Leave empty with a location set to get everything in that area.

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

City or area, e.g. London. Leave empty to search the whole country.

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

Which national Indeed to search. This sets the market, language and currency of the results.

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

Additional keywords to run in the same job, each using the location above. Through the API an entry may also be an object with its own keyword and location.

## `maxItemsPerSearch` (type: `integer`):

Indeed stops paging at roughly 1,000 results per search. 0 means take everything it offers.

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

A ceiling for the whole run, across every search. 0 means no ceiling.

## `radius` (type: `integer`):

How far from the location to search.

## `radiusUnit` (type: `string`):

Miles or kilometres.

## `excludeExpired` (type: `boolean`):

Drop listings Indeed has marked expired.

## `onlyRemote` (type: `boolean`):

Keep only listings tagged remote.

## `onlyEmployerStatedPay` (type: `boolean`):

Keep only listings where the employer published the salary. Otherwise the range may be Indeed's own estimate, which each row labels.

## `minPay` (type: `integer`):

Drop listings whose top of range is below this, in the country's currency. 0 keeps everything, including listings with no pay stated.

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

The full advert as plain text. Included at no extra request cost.

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

The advert with its original formatting. Makes rows considerably larger.

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

How many requests run at once across the whole run. The default is the fastest level measured clean; lower it only if a run reports refusals or errors.

## `delayMs` (type: `integer`):

Optional pause before each call, in milliseconds, up to 500. 0 (the default) runs as fast as the parallel limit allows.

## Actor input object example

```json
{
  "keyword": "paralegal",
  "location": "London",
  "country": "GB",
  "searches": [],
  "maxItemsPerSearch": 200,
  "maxItems": 0,
  "radius": 25,
  "radiusUnit": "MILES",
  "excludeExpired": true,
  "onlyRemote": false,
  "onlyEmployerStatedPay": false,
  "minPay": 0,
  "includeDescription": true,
  "includeDescriptionHtml": false,
  "maxConcurrency": 4,
  "delayMs": 0
}
```

# Actor output Schema

## `dataset` (type: `string`):

Indeed jobs — every row the run returned.

## `stats` (type: `string`):

Counts for the run: what was searched, fetched, filtered and returned.

# 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 = {
    "keyword": "paralegal",
    "location": "London"
};

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

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

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

```

## Python example

```python
from apify_client import ApifyClient

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

# Prepare the Actor input
run_input = {
    "keyword": "paralegal",
    "location": "London",
}

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

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

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

```

## CLI example

```bash
echo '{
  "keyword": "paralegal",
  "location": "London"
}' |
apify call boncross/indeed-jobs --silent --output-dataset

```

## MCP server setup

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

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

## OpenAPI specification

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