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

Scrape Indeed job listings: title, company, salary, description and more. Search by keywords, location and country, or start from Indeed URLs.

- **URL**: https://apify.com/osamih/indeed-scraper.md
- **Developed by:** [Osamih](https://apify.com/osamih) (community)
- **Categories:** Jobs
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

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

## 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

Extract job listings from **Indeed**, the world's largest job site — job title, company, location, salary, full description, ratings, posting date and more. Search by keywords and location across **57 countries**, or start directly from Indeed URLs.

Indeed shut down its public Job Search API, so scraping is the only practical way to get this data programmatically. This Actor handles the hard parts for you: browser fingerprinting, residential proxy rotation and Cloudflare anti-bot protection.

### Features

- 🔍 **Keyword + location search** — same as Indeed's "what / where" boxes
- 🌍 **57 countries** — every Indeed country site, selected with a single input field
- 🎛️ **Search filters** — date posted, job type, experience level, minimum pay, remote-only, radius, sort order
- 🔗 **Start URLs** — scrape any Indeed search page, company jobs page or single job URL
- 🏢 **Company details** *(optional)* — rating, review count, industry, size, HQ, revenue, description
- ↪️ **External apply links** *(optional)* — resolves Indeed's redirect to the employer's real application URL
- ♻️ **Deduplication** — each job saved once per run, even if it appears in multiple searches
- 📦 **Structured output** — clean JSON via API, or export CSV / Excel / HTML from the Apify Console

### Output example

```json
{
    "positionName": "Senior Web Developer",
    "id": "8a2f4e15c3b7d901",
    "url": "https://www.indeed.com/viewjob?jk=8a2f4e15c3b7d901",
    "company": "Acme Corp",
    "companyLogo": "https://d2q79iu7y748jz.cloudfront.net/s/_squarelogo/acme.png",
    "location": "San Francisco, CA 94105",
    "salary": "$120,000 - $150,000 a year",
    "jobType": ["Full-time"],
    "rating": 4.2,
    "reviewsCount": 128,
    "postedAt": "3 days ago",
    "postingDateParsed": "2026-07-02T08:00:00.000Z",
    "description": "We are looking for a Senior Web Developer…",
    "descriptionHTML": "<p>We are looking for a Senior Web Developer…</p>",
    "externalApplyLink": "https://careers.acme.com/jobs/1234",
    "isExpired": false,
    "scrapedAt": "2026-07-05T12:34:56.789Z",
    "searchInput": { "position": "web developer", "location": "San Francisco", "country": "US" },
    "companyInfo": null
}
```

`externalApplyLink` is the employer's own application URL. Jobs that are applied to through
Indeed Apply have no such URL, so the field is `null` for them.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `position` | string | – | Keywords, e.g. `web developer` |
| `location` | string | – | City, state or zip, e.g. `San Francisco` |
| `country` | string | `US` | Country code — determines the Indeed domain |
| `maxItemsPerSearch` | integer | `50` | Result cap per search / per start URL |
| `datePosted` | string | `any` | Posting window: `1`, `3`, `7` or `14` days |
| `jobType` | string | `any` | `fulltime`, `parttime`, `contract`, `temporary`, `internship`, `permanent`, `apprenticeship`, `seasonal`, `commission`, `subcontract` |
| `experienceLevel` | string | `any` | `entry_level`, `mid_level`, `senior_level` |
| `minSalary` | string | – | Minimum pay in local format, e.g. `$60,000` or `£35,000` |
| `remote` | boolean | `false` | Remote / work-from-home jobs only |
| `radius` | integer | – | Distance from `location` in miles (max 100) |
| `sortBy` | string | `relevance` | `relevance` or `date` (newest first) |
| `includeSimilarJobs` | boolean | `false` | Also return near-duplicates Indeed normally hides |
| `startUrls` | array | – | Indeed URLs to scrape directly |
| `parseCompanyDetails` | boolean | `false` | Attach `companyInfo` from the employer's Indeed page |
| `saveOnlyUniqueItems` | boolean | `true` | Deduplicate jobs across searches |
| `followApplyRedirects` | boolean | `false` | Resolve external apply links to the final employer URL |
| `maxConcurrency` | integer | `5` | Parallel pages (higher = faster, more blocks) |
| `proxyConfiguration` | object | Apify residential | Proxy settings |

Minimal input:

```json
{ "position": "web developer", "location": "San Francisco", "country": "US", "maxItemsPerSearch": 50 }
```

### How many results can I get?

Indeed itself never serves more than **~1,000 results for a single search**, no matter how you paginate. To go deeper, split one broad search into several narrower ones — by city, radius, or date posted — and run them as separate searches or start URLs. Duplicates across searches are removed automatically when `saveOnlyUniqueItems` is on.

### Proxy requirements

Indeed uses aggressive anti-bot protection (Cloudflare). **Residential proxies are effectively required** — datacenter IPs get blocked almost immediately. The default input already selects the Apify `RESIDENTIAL` proxy group; make sure it is enabled for your Apify account. Expect roughly 0.3–1 GB of residential traffic per 1,000 jobs (the Actor blocks images, fonts and trackers to keep this low).

### Integrations & API

Run the Actor from the [Apify API](https://docs.apify.com/api/v2), the JavaScript / Python clients, or on a [schedule](https://docs.apify.com/platform/schedules). Connect webhooks to trigger downstream workflows when a run finishes, and read results straight from the run's dataset in JSON, CSV or Excel.

```bash
## Run via API and get the dataset
curl "https://api.apify.com/v2/acts/<user>~indeed-scraper/run-sync-get-dataset-items?token=<API_TOKEN>" \
  -X POST -H 'Content-Type: application/json' \
  -d '{"position":"data engineer","country":"US","maxItemsPerSearch":100}'
```

### Limitations & fair use

- Only publicly visible job data is collected — nothing behind a login, and no personal data.
- Job listings change fast; results reflect the moment of the run.
- Company-jobs start URLs (`/cmp/…/jobs`) are supported on a best-effort basis.
- `startUrls` entries with `requestsFromUrl` (remote URL lists) are not supported yet.
- Respect Indeed's public data and applicable laws when using the output.

### Troubleshooting

- **Run finishes with 0 items and "blocks retried" > 0** → your proxy configuration has no residential access. Switch the proxy group to `RESIDENTIAL`.
- **Fewer items than `maxItemsPerSearch`** → the search genuinely has fewer results, or the ~1,000 cap was hit.
- **`PARSER_DEGRADED` warnings in the log** → Indeed changed its page markup; the Actor falls back to secondary extraction. If fields go missing, please report it — an HTML snapshot is saved to the run's key-value store for diagnosis.

### Changelog

#### 0.1.0

- Initial release: keyword/location/country search, start URLs, job details, company enrichment, apply-link resolution, deduplication, 57 countries.

# Actor input Schema

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

Job title or any combination of keywords — same as Indeed's <b>what</b> search box. Leave empty if you only use Start URLs.

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

City, state, zip code or locality — same as Indeed's <b>where</b> search box. Leave empty for a country-wide search.

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

Which Indeed country site to search. Determines the Indeed domain (e.g. <code>uk.indeed.com</code> for the United Kingdom).

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

Maximum number of jobs to save for each keyword search and each Start URL. Note that Indeed itself caps any single search at roughly 1,000 results — for deeper coverage split your search by location or date.

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

Only return jobs posted within this window — Indeed's <b>Date posted</b> filter.

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

Filter by employment type — Indeed's <b>Job type</b> filter. Indeed supports only one job type per search. Types beyond the first five are only available on some country sites.

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

Filter by required experience — Indeed's <b>Experience level</b> filter.

## `minSalary` (type: `string`):

Only return jobs paying at least this amount — Indeed's <b>Pay</b> filter. Write it the way it appears on your Indeed site, e.g. <code>$60,000</code>, <code>£35,000</code> or <code>€45.000</code>. Indeed matches it against its own salary estimates, so jobs without pay data may be excluded.

## `remote` (type: `boolean`):

Only return remote / work-from-home jobs. Best-effort — relies on Indeed's remote filter code, which can vary by country.

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

Distance from the <b>Location</b> to include, in miles. Ignored when no location is set. Indeed snaps to its allowed steps (0, 5, 10, 15, 25, 50, 100).

## `sortBy` (type: `string`):

Result ordering. <b>Relevance</b> is Indeed's default; <b>Date</b> returns the newest jobs first.

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

Indeed URLs to scrape directly — search result URLs, company job listing URLs (<code>/cmp/…/jobs</code>) or single job URLs (<code>/viewjob?jk=…</code>). When set, they are scraped in addition to the keyword search (each URL gets its own <b>Max items</b> budget).

## `parseCompanyDetails` (type: `boolean`):

Also visit the Indeed company page of each employer and attach a <code>companyInfo</code> object (rating, size, industry, HQ, description…). Each unique company page is fetched only once per run. Slower and uses more proxy traffic.

## `saveOnlyUniqueItems` (type: `boolean`):

Deduplicate jobs by Indeed job key across all searches and Start URLs in this run.

## `followApplyRedirects` (type: `boolean`):

Follow the redirects of Indeed's external apply link and output the final employer URL instead of the Indeed redirect. Adds one extra request per job.

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

How many pages are scraped in parallel. Higher is faster but increases proxy usage and the chance of blocking.

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

Proxies to use. <b>Residential proxies are strongly recommended</b> — Indeed blocks datacenter IP addresses.

## Actor input object example

```json
{
  "position": "web developer",
  "location": "San Francisco",
  "country": "US",
  "maxItemsPerSearch": 50,
  "datePosted": "any",
  "jobType": "any",
  "experienceLevel": "any",
  "remote": false,
  "sortBy": "relevance",
  "parseCompanyDetails": false,
  "saveOnlyUniqueItems": true,
  "followApplyRedirects": false,
  "maxConcurrency": 5,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# 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": "web developer",
    "location": "San Francisco",
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

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

# Run the Actor and wait for it to finish
run = client.actor("osamih/indeed-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 '{
  "position": "web developer",
  "location": "San Francisco",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call osamih/indeed-scraper --silent --output-dataset

```

## MCP server setup

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

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/fTuR6hcVtSoHGBTN9/builds/7I2AVCl6dKglhsSjc/openapi.json
