# Indeed Jobs Scraper - Salaries, Benefits & Ratings (`zyra/indeed-jobs-scraper`) Actor

Search Indeed in 62 countries by title and location with the filters Indeed itself applies. Full descriptions, pay, benefits, company rating and apply links in Bright Data's Indeed format.

- **URL**: https://apify.com/zyra/indeed-jobs-scraper.md
- **Developed by:** [Zyra](https://apify.com/zyra) (community)
- **Stats:** 9 total users, 8 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.10 / 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.

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 Jobs Scraper

**Indeed job listings with full descriptions, salaries, benefits and company ratings, from 62 countries.**

Enter a job title and a location. You get back every matching Indeed job as a flat row: title, company, pay, benefits, shift schedule, company rating, the full description and the apply link.

A run of 100 jobs takes about 2 seconds, and 1,000 jobs about 15.

### What you can do with it

- **Benchmark pay.** Salary ranges as numbers, with the period (hour, year…), the currency, and whether Indeed estimated the figure or the employer stated it.
- **Track hiring.** See who is hiring for a role, where, and since when.
- **Screen employers.** Every job carries the company's Indeed rating and review count, plus its size, revenue and industry.
- **Feed a job board or a model.** Flat rows with stable ids, ready for a spreadsheet, a database or a dataframe.

### Why this one

- **Drop-in for Bright Data.** The first 28 fields have the same names, order and formats as Bright Data's Indeed dataset, so existing pipelines keep working.
  - It is also more complete: in a 600-job comparison we filled `apply_link` on 100% of jobs (Bright Data: 2%), `company_website` on 79% (0%), `salary_formatted` on 78% (46%) and `company_rating` on 88% (72%).
- **Live jobs.** Every job comes from a search run at that moment, so none are expired.
- **Filters applied by Indeed.** Date posted, job type, remote, hybrid or in person, experience level, radius and Easy Apply are all applied by Indeed during the search. A narrower search is faster and cheaper, and matches what you'd see on the site.
- **Full descriptions without an extra fetch.** Each search result already includes the complete description, as HTML and as plain text.
- **Public data only.** The actor sees what a signed-out visitor sees, and never signs in.

### Input

| Field | Example | Notes |
| --- | --- | --- |
| `title` | `web developer` | Job title, keywords or company. Indeed's syntax works: `"exact phrase"`, `-exclude`, `company:(Google)`, `title:(nurse)`. |
| `location` | `New York, NY` | City, state, zip code or `remote`. |
| `country` | `us` | One of 62 Indeed countries: `us`, `uk`, `in`, `ca`, `de`, `fr`, `au`… |
| `limit` | `100` | 1–1,000. Default 100. |
| `datePosted` | `7` | Last `1`, `3`, `7` or `14` days. |
| `jobType` | `fulltime` | `fulltime`, `parttime`, `contract`, `internship`, `temporary`, `seasonal`, `perdiem`, `apprenticeship`. |
| `workSetting` | `remote` | `remote`, `hybrid`, `onsite`. |
| `experienceLevel` | `senior` | `entry`, `mid`, `senior`, `no_experience`, `under_1_year`. **Indeed US only.** |
| `radius` | `25` | Miles around the location: `0`, `5`, `10`, `15`, `25`, `35`, `50`, `100`. |
| `easyApplyOnly` | `false` | Only jobs you can apply to on Indeed. |
| `proxyConfiguration` | Apify Proxy | Leave as it is. Indeed refuses connections from the Apify platform's own network, so runs go through Apify Proxy. |

You need a title, a location or both. Filters combine with AND.

```json
{ "title": "case manager", "location": "Grand Rapids, MI", "country": "us", "limit": 10, "jobType": "fulltime" }
```

### Output

One item per job. Real examples: [sample\_output.json](sample_output.json).

**Bright Data–compatible fields** (same names, order and formats):

`jobid`, `company_name`, `date_posted_parsed`, `job_title`, `description_text`, `benefits`, `qualifications`, `job_type`, `location`, `salary_formatted`, `company_rating`, `company_reviews_count`, `country`, `date_posted`, `description`, `region`, `company_link`, `company_website`, `domain`, `apply_link`, `srcname`, `url`, `is_expired`, `discovery_input`, `job_location`, `job_description_formatted`, `logo_url`, `shift_schedule`

**Extra detail:**

- **Pay:** `salary_min`, `salary_max`, `salary_period`, `salary_currency`, `salary_is_estimated`
- **Place:** `city`, `postal_code`, `street_address`, `country_name`
- **Company:** `company_size`, `company_revenue`, `company_industry`, `company_description`, `company_ceo`, `company_address`
- **Job:** `work_setting`, `experience_level`, `is_remote`, `attributes`, `emails`
- **Collection:** `date_on_indeed`, `timestamp`

```json
{
  "jobid": "bc3930a1d889a475",
  "job_title": "ACT IDDT Case Manager (26-133)",
  "company_name": "Network180",
  "salary_formatted": "$21.99 - $29.15 an hour",
  "company_rating": 3.5,
  "company_reviews_count": 31,
  "benefits": [
    "License reimbursement",
    "Paid holidays",
    "Loan repayment program"
  ],
  "shift_schedule": [
    "Weekends as needed",
    "Evenings as needed"
  ],
  "date_posted": "3 days ago",
  "url": "https://www.indeed.com/viewjob?jk=bc3930a1d889a475"
}
```

**Before comparing two salaries, check `salary_period`:** hourly and yearly pay appear in the same results. **`salary_is_estimated`** tells you whether a figure is Indeed's estimate rather than the employer's.

The run summary (what was requested, how many jobs arrived, what was charged) is in the key-value store under `OUTPUT`.

### Limits

- **One search tops out at about 750–970 jobs.** Indeed stops paging there, however many jobs match. For more, run several searches with narrower locations.
- **A city goes deeper than a state.** Nurse in Los Angeles, CA returned 862 jobs; nurse in California returned 398.
- **A search without a location returns only about 100 jobs.** On Indeed Canada it returns none, so the actor asks for a city.
- **Some fields depend on what the employer entered.** For example, salary appears on about 75% of jobs, and the company rating only when the company has reviews on Indeed.

### Pricing

Pay per result: you are charged per job delivered. A run stops once it has spent its maximum charge, and it never charges for a job it didn't deliver.

***

### Development

```bash
pip install -r requirements-dev.txt
pytest tests/                         # offline: client, inputs, scrape loop, billing
INDEED_LIVE=1 pytest tests/           # also hits Indeed: every filter, all 62 countries
python tools/gen_dataset_schema.py    # regenerate .actor/dataset_schema.json from the Job fields
python tools/stress_test.py           # concurrency ramp against Indeed
```

| Path | What it is |
| --- | --- |
| `src/indeed_client.py` | The Indeed client: a plain library with no Apify dependency. |
| `src/inputs.py` | Actor input → validated run configuration. |
| `src/scrape.py` | The search loop: retries, and resuming from the last cursor after a failure. |
| `src/main.py` | The run: delivery, push-then-charge, budget limits, run summary. |
| `research/` | R\&D notes, raw API samples and the Bright Data sample. Not shipped in the image. |

- **Billing event:** `job-listing`, charged per job after each page is pushed. The start fee is Apify's built-in `apify-actor-start`, which the platform charges itself.
- **API key risk:** the client uses the API key built into Indeed's iOS app. That key is not a published API, so Indeed can change it at any time. The headers are in one place, `API_HEADERS` in `src/indeed_client.py`.

# Actor input Schema

## `title` (type: `string`):

Job title, keywords or company, as you'd type them on Indeed, e.g. "web developer". Indeed's search syntax works too: "exact phrase", -exclude, company:(Google), title:(nurse). Leave empty to get every job in the location.

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

City, state, zip code or "remote", e.g. "New York, NY". A city goes deeper than a whole state (nurse in Los Angeles: 862 jobs; in California: 398). With no location, Indeed returns only about 100 jobs, and Indeed Canada returns none.

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

Which country's Indeed to search. It sets the job market, the currency and the links.

## `limit` (type: `integer`):

The most jobs to return. Indeed stops a single search at about 750-970 jobs, so for more, run several searches with narrower locations.

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

Only jobs posted in the last N days.

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

Employment type.

## `workSetting` (type: `string`):

Remote, hybrid or in person. "Remote" means only jobs that Indeed tags as remote.

## `experienceLevel` (type: `string`):

Level of the role. Indeed US only: other countries' jobs carry no experience tags, so the run refuses this filter there.

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

Distance around the location, in miles. Indeed's default is 25.

## `easyApplyOnly` (type: `boolean`):

Only jobs you can apply to on Indeed itself.

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

Where requests to Indeed come from. Apify Proxy is needed on the Apify platform, because Indeed refuses connections from the platform's own network. Switch to residential if datacenter IPs get refused.

## Actor input object example

```json
{
  "title": "web developer",
  "location": "New York, NY",
  "country": "us",
  "limit": 100,
  "datePosted": "",
  "jobType": "",
  "workSetting": "",
  "experienceLevel": "",
  "radius": "",
  "easyApplyOnly": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

Every job scraped, one item each.

## `runSummary` (type: `string`):

What was requested, what arrived, how many requests it took and what was charged.

# 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 = {
    "title": "web developer",
    "location": "New York, NY",
    "limit": 100,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("zyra/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 = {
    "title": "web developer",
    "location": "New York, NY",
    "limit": 100,
    "proxyConfiguration": { "useApifyProxy": True },
}

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

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

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

```

## CLI example

```bash
echo '{
  "title": "web developer",
  "location": "New York, NY",
  "limit": 100,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call zyra/indeed-jobs-scraper --silent --output-dataset

```

## MCP server setup

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

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

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/uqn1KJicWJXofgUoN/builds/12sVdDn1hKEFWDqex/openapi.json
