# Indeed Jobs Scraper - Companies, Reviews, Salaries (`autofacts/indeed-jobs-scraper-companies-reviews-salaries`) Actor

Jobs and the employers behind them, in one run. Full descriptions, parsed salaries, company profiles, employee reviews, salary tables and Q\&A — joined on Indeed's own employer id, not on company name. 62 countries, no login, pay per result. Only-new mode returns just what changed.

- **URL**: https://apify.com/autofacts/indeed-jobs-scraper-companies-reviews-salaries.md
- **Developed by:** [Richard Feng](https://apify.com/autofacts) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 85.7% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 jobs

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?

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 Scraper — Jobs, Employers, Reviews, Salaries & Q\&A

**Indeed Scraper** extracts job listings from [Indeed](https://www.indeed.com/) with full
descriptions and parsed salaries — and, in the same run, the **employers behind them**:
company profiles, employee reviews, salary tables and community Q\&A, joined on Indeed's
own employer id. 62 country domains, no login required, pay per result.

Most Indeed actors do one side or the other. This one runs a keyword search and hands back
the employers that search discovered, already linked to their postings.

### What does Indeed Scraper do?

Type a job title and a location, press **Start**, and get structured job data back — or
paste an Indeed URL and it works out what you meant. It accepts a `/jobs` search with your
filters already in the query string, a `/viewjob` posting, a `/cmp/…` company page or tab,
or a `/career/…` salary page.

Switch on `includeCompanyDetails` and every employer the search found comes back too, with
whichever of `profile`, `reviews`, `salaries` and `qna` you ask for.

### What data can I extract from Indeed?

<table>
<tr><td>🧑‍💼 Job title & normalized title</td><td>🏢 Company profile, size & revenue</td></tr>
<tr><td>📃 Full job description (HTML + text)</td><td>⭐️ Employee reviews & category ratings</td></tr>
<tr><td>💵 Parsed salary range & period</td><td>💰 Salary tables by role and employer</td></tr>
<tr><td>📍 City, state, postcode & remote status</td><td>❓ Community questions & top answers</td></tr>
<tr><td>🎁 Benefits, requirements & shift tags</td><td>👔 CEO, HQ, founding year & website</td></tr>
<tr><td>🕐 Posting date & expiry</td><td>📧 Employer contact address (optional)</td></tr>
</table>

### Why use this scraper?

- **Both sides of Indeed in one run.** Jobs *and* the employers behind them — profiles,
  reviews, salary tables, Q\&A — joined on Indeed's own employer id, not on company name.
- **Every filter is verified, not assumed.** Indeed silently ignores URL parameters it does
  not recognise, so each filter here was measured against a deliberate control before being
  shipped. Controls that turned out to do nothing were **removed rather than left in place**
  looking like they worked.
- **Published coverage numbers.** On a search Indeed reported as 2,676 postings: **737
  unique jobs in 41 requests** paging alone, **1,388 in 369 requests** with widening on.
  Every run logs what it actually collected against what Indeed claimed.
- **Full descriptions at the base rate.** Job text is fetched in batches of 40, so it is
  included rather than sold as a per-job add-on.
- **Widening that respects your query.** A broad search is re-run as narrower slices — by
  location, date, job type, pay and education — and slices that are still too large are
  split again. A search you already scoped to a city is never silently widened.
- **Scheduled monitoring.** Remember what previous runs returned and receive only new
  postings. Skipped jobs cost no requests and no charge.
- **Strict matching.** Indeed pads every result page with loosely-related and sponsored
  postings. Drop them — judged against the full description, not the snippet.
- **62 country domains**, with currency, locale, and miles or kilometres handled for you.
- **Clean output.** `source.canonicalUrl` is always the public page a person would open.
  The endpoints the actor fetches internally, and Indeed's tracking fields, never reach
  your data.

### Use cases

- **Recruitment & talent intelligence** — see who is hiring for which roles, at what pay,
  in which markets, and what their employees say about working there.
- **Salary benchmarking** — parsed pay per posting, plus company-level and national salary
  tables by job title and location.
- **Market & competitor research** — an employer's open headcount, ratings, size, revenue
  and hiring velocity, refreshed on a schedule you control.
- **Job boards & aggregators** — a clean feed filtered to what actually matches, delivering
  only what is new since your last run.
- **Lead generation** — every posting is a company spending money to hire, with optional
  business contact details from the employer's own website.
- **Candidate matching** — score every posting 0-100 against a skill list and see which
  skills are missing.

### How do I use Indeed Scraper?

1. Create a free [Apify](https://apify.com/) account.
2. Open the actor and enter a job title under **Positions / keywords** and a **Location**.
3. Optionally narrow with date posted, job type, remote, pay or education — or switch on
   **Include company details** to get the employers as well.
4. Click **Start** and wait for the run to finish.
5. Download the data as JSON, CSV, Excel, XML or HTML, or pull it from the API.

Leave the location empty to search a whole country. Residential proxies are strongly
recommended — Indeed challenges most datacenter traffic.

### Input

Everything is optional except giving the actor something to search — `keywords`,
`startUrls`, `jobKeys`, `companyUrls` or `salaryJobTitles`.

| Parameter | Type | Description |
| :--- | :--- | :--- |
| `scrapeMode` | String | What this run does: `jobs` (keyword search, the default), `company` (employer pages from `companyUrls`), or `careerSalary` (national pay tables). Fields belonging to the other modes are ignored, and the log says what it skipped. |
| `keywords` | Array | Job titles, skills or company names. Each is searched separately. |
| `location` | String | City, state, ZIP or region. Leave empty to search a whole country. |
| `country` | String | Which Indeed country site to search (default `US`). 62 supported. |
| `startUrls` | Array | Any Indeed URL: a `/jobs` search with filters, a `/viewjob` posting, a `/cmp/…` company page or tab, or a `/career/…` salary page. |
| `jobKeys` | Array | Look jobs up directly by Indeed job key (the `jk` value in a job URL). |
| `datePosted` | String | `1`, `3`, `7` or `14` days. |
| `jobType` | Array | `fulltime`, `parttime`, `contract`, `temporary`, `internship`, `permanent`, `new_grad`, `commission`. Indeed takes one type per search, so picking several runs one search per type and widens the result set. |
| `remoteWork` | String | `remote` or `hybrid`. Indeed filters remote itself; it has no working hybrid filter, so hybrid is applied to the results and reads more pages for the same number of rows. |
| `education` | String | `high_school_degree` … `doctoral_degree`. |
| `salaryMin` | Integer | Annual pay threshold, in the country's currency. |
| `onlyWithSalary` | Boolean | Keep only postings that state or estimate pay. |
| `radius` / `radiusUnit` | Integer / String | Search radius, in `miles` or `km`. |
| `sort` | String | `relevance` or `date`. |
| `maxItems` | Integer | Total jobs to return (default `100`). |
| `fanOut` | Boolean | Re-run broad searches as narrower slices (default `true`). |
| `maxFanOutRequests` | Integer | Budget for that widening. |
| `deepSearch` | Boolean | Expand each keyword into related phrasings. |
| `strictMatch` | Boolean | Drop postings that do not contain your keywords. |
| `uniqueCompanies` | Boolean | At most one posting per employer. |
| `resumeKeywords` | Array | Score every posting 0-100 against a skill list. Not a filter. |
| `storeName` | String | Remember what this search returned, for scheduled runs. |
| `onlyNewJobs` | Boolean | Return only postings no earlier run returned. Needs `storeName`. |
| `includeDescription` | Boolean | Full job text, batched (default `true`). |
| `includeJobDetail` | Boolean | Extra per-job fetch for benefits and qualifications. |
| `includeCompanyDetails` | Boolean | Add the employer's Indeed profile. |
| `companyDataTypes` | Array | `profile`, `reviews`, `salaries`, `qna`. |
| `enrichEmails` | Boolean | Read each employer's own site for a business contact address. |
| `companyUrls` | Array | Scrape employers directly, without a job search. |
| `salaryJobTitles` | Array | National salary tables by role. |
| `salaryLocations` | Array | Restrict those salary tables to given locations. |
| `maxReviews` / `maxQuestions` | Integer | Cap review and Q\&A rows per employer. |
| `sessionCookies` | String | Cookies from a browser already signed in to Indeed. Never asks for a password. |
| `proxyConfiguration` | Object | **Residential proxies are strongly recommended.** |

#### Input examples

| Use case | Input JSON |
| :--- | :--- |
| Jobs by keyword and location | `{ "keywords": ["registered nurse"], "location": "Texas", "maxItems": 200 }` |
| Search a different country | `{ "keywords": ["software engineer"], "location": "London", "country": "GB" }` |
| Only recent, full-time, remote | `{ "keywords": ["data analyst"], "datePosted": "3", "jobType": ["fulltime"], "remoteWork": "remote" }` |
| Several employment types at once | `{ "keywords": ["welder"], "location": "Houston, TX", "jobType": ["fulltime", "contract", "temporary"] }` |
| Roles above a salary | `{ "keywords": ["product manager"], "location": "Seattle, WA", "salaryMin": 150000 }` |
| Only postings that state pay | `{ "keywords": ["warehouse associate"], "location": "Dallas, TX", "onlyWithSalary": true }` |
| Clean, relevant results only | `{ "keywords": ["registered nurse"], "location": "Texas", "strictMatch": true, "uniqueCompanies": true }` |
| Score jobs against a CV | `{ "keywords": ["backend engineer"], "resumeKeywords": ["Python", "Kubernetes", "PostgreSQL"] }` |
| Go wide on a national search | `{ "keywords": ["nurse"], "location": "", "fanOut": true, "maxFanOutRequests": 40, "maxItems": 2000 }` |
| Widen a narrow keyword | `{ "keywords": ["software engineer"], "location": "Austin, TX", "deepSearch": true, "maxItems": 500 }` |
| Daily monitor — only new jobs | `{ "keywords": ["python developer"], "location": "Denver, CO", "storeName": "python-denver", "onlyNewJobs": true }` |
| Jobs plus employer profiles | `{ "keywords": ["data engineer"], "location": "Chicago, IL", "includeCompanyDetails": true }` |
| Everything about employers | `{ "keywords": ["dentist"], "location": "Phoenix, AZ", "includeCompanyDetails": true, "companyDataTypes": ["profile", "reviews", "salaries", "qna"] }` |
| Employers only, no job search | `{ "scrapeMode": "company", "companyUrls": [{ "url": "https://www.indeed.com/cmp/Google" }], "companyDataTypes": ["profile", "reviews"], "maxReviews": 200 }` |
| Employers *alongside* a job search | `{ "keywords": ["data engineer"], "location": "Chicago, IL", "includeCompanyDetails": true }` |
| National salary tables | `{ "scrapeMode": "careerSalary", "salaryJobTitles": ["software engineer", "registered nurse"], "salaryLocations": ["New York, NY"] }` |
| Sales leads with contacts | `{ "keywords": ["hvac technician"], "location": "Tampa, FL", "includeCompanyDetails": true, "enrichEmails": true }` |
| Paste an Indeed URL | `{ "startUrls": [{ "url": "https://www.indeed.com/jobs?q=nurse&l=Austin%2C+TX&fromage=3" }] }` |

### Output

Data is stored in the default dataset. Five record types share it, told apart by
`recordType`, each with its own view in the Console: **Jobs**, **Companies**, **Reviews**,
**Salaries** and **Q\&A**.

`source.canonicalUrl` is always the public Indeed page a person would open. The endpoints
the actor fetches internally, and Indeed's tracking fields, never appear in your data.

Jobs join to companies on `company.fccId` = the company record's `source.encryptedFccId`.

<details>
<summary><strong>View Job Output Example</strong></summary>

```json
{
  "recordType": "job",
  "source": {
    "jobKey": "f687cbdb754c430a",
    "canonicalUrl": "https://www.indeed.com/viewjob?jk=f687cbdb754c430a",
    "country": "US",
    "domain": "www.indeed.com",
    "scrapedAt": "2026-08-27T03:23:45.483Z"
  },
  "title": "Registered Nurse (Multiple Openings)",
  "normalizedTitle": "Registered Nurse",
  "company": {
    "name": "AdventHealth Central Texas",
    "fccId": "11994ad5a40ae7aa",
    "encryptedId": "efb0d1f7cb4d4009",
    "rating": 3.7,
    "reviewCount": 4166
  },
  "location": {
    "formatted": "Killeen, TX 76549",
    "city": "Killeen",
    "state": "TX",
    "postalCode": "76549",
    "country": "US",
    "isRemote": false
  },
  "salary": {
    "min": 33.28,
    "max": 61.91,
    "currency": "USD",
    "period": "HOURLY",
    "text": "$33.28 - $61.91 an hour",
    "isEstimate": true
  },
  "jobTypes": ["Full-time", "Part-time"],
  "benefits": [
    "Health insurance", "Paid parental leave", "Tuition reimbursement",
    "403(b)", "Paid time off", "Relocation assistance"
  ],
  "requirements": ["Nursing", "BLS Certification", "ACLS Certification"],
  "attributes": ["Day shift", "Night shift", "3x12"],
  "postedAt": "2026-08-26T05:00:00.000Z",
  "postedRelative": "Just posted",
  "expired": false,
  "isSponsored": true,
  "hiringDemand": { "highVolume": false, "hiresNeeded": "20" },
  "apply": { "isIndeedApply": false },
  "searchContext": { "keyword": "registered nurse", "page": 1 },
  "description": {
    "html": "<p>…</p>",
    "text": "AdventHealth Central Texas seeks to hire a Registered Nurse who will…"
  }
}
```

`salary.isEstimate` distinguishes Indeed's own estimate from an employer-stated range. `attributes` carries shift and schedule tags. With `resumeKeywords` set, each job also gains `resumeMatchScore`, `resumeMatchedKeywords` and `resumeMissingKeywords`.

</details>

<details>
<summary><strong>View Company Output Example</strong></summary>

```json
{
  "recordType": "company",
  "source": {
    "encryptedFccId": "376f9ba19c70ac69",
    "canonicalUrl": "https://www.indeed.com/cmp/Roadrunner-Transportation-Services%2C-Inc.",
    "country": "US",
    "scrapedAt": "2026-08-27T03:23:25.727Z"
  },
  "name": "Roadrunner",
  "rating": 2.7,
  "reviewCount": 471,
  "happinessScore": 64,
  "happinessGrade": "POOR",
  "industry": "Transportation & Logistics",
  "size": "1,001–5,000 employees",
  "revenue": "$500M to $1B",
  "founded": "1984",
  "ceo": "Chris Jamroz, CEO of Roadrunner Freight",
  "headquarters": "1431 Opus Place, Ste 530, Downers Grove, IL 60515",
  "website": "https://roadrunnerltl.com/",
  "counts": { "reviews": 471, "jobs": 270, "faq": 91 },
  "contactWebsite": "https://roadrunnerltl.com/",
  "contactEmails": ["careers@example.com"],
  "paymentProcessors": ["Stripe"]
}
```

`contactEmails`, `contactWebsite` and `paymentProcessors` appear only with `enrichEmails` enabled, and only when the employer's site publishes them.

</details>

<details>
<summary><strong>View Review Output Example</strong></summary>

```json
{
  "recordType": "review",
  "source": {
    "reviewId": "cb15e25596400949",
    "canonicalUrl": "https://www.indeed.com/cmp/Google/reviews/nice-comp-and-brand-value?id=cb15e25596400949",
    "companyName": "Google",
    "country": "US",
    "scrapedAt": "2026-08-27T03:22:29.962Z"
  },
  "title": "Nice comp and brand value",
  "text": "Management is not good, career progression is slow…",
  "overallRating": 5,
  "ratings": {
    "workLifeBalance": 3,
    "compensationBenefits": 4,
    "jobSecurityAdvancement": 3,
    "management": 2,
    "cultureValues": 2
  },
  "jobTitle": "Product Manager",
  "location": "San Jose, CA",
  "isCurrentEmployee": true,
  "submittedAt": "July 12, 2026",
  "helpfulCount": 0,
  "unhelpfulCount": 0
}
```

Category ratings appear only where the reviewer supplied them.

</details>

<details>
<summary><strong>View Salary Output Example</strong></summary>

```json
{
  "recordType": "salary",
  "source": {
    "canonicalUrl": "https://www.indeed.com/career/software-engineer/salaries",
    "country": "US",
    "scrapedAt": "2026-08-27T03:22:25.121Z"
  },
  "scope": "career",
  "jobTitle": "Software Engineer",
  "location": "United States",
  "average": 135623,
  "min": 80144,
  "max": 229507,
  "currency": "USD",
  "period": "YEARLY",
  "sampleSize": 39364,
  "topPayingCompanies": [
    { "name": "CHAI", "salary": 381250 }
  ],
  "topPaidCities": [
    { "city": "San Jose, CA", "salary": 181115 },
    { "city": "New York, NY", "salary": 171108 }
  ]
}
```

`scope` is `career` for national tables by job title, or `company` for pay by role at one employer. Company-scope rows carry `companyName` and `average`; the range and top-payer fields are national only.

</details>

<details>
<summary><strong>View Q&amp;A Output Example</strong></summary>

```json
{
  "recordType": "qna",
  "source": {
    "questionId": "1h5cnft3jj4op800",
    "canonicalUrl": "https://www.indeed.com/cmp/Google/faq/what-about-the-english-level…?quid=1h5cnft3jj4op800",
    "companyName": "Google",
    "country": "US",
    "scrapedAt": "2026-08-27T03:23:04.174Z"
  },
  "question": "What about the English level? do you need a related license/degree such as IELTS to get an h4 visa?",
  "askedAt": "July 15, 2023",
  "answerCount": 2,
  "topAnswer": {
    "text": "yes, we need to be excellent with communications",
    "answerId": "1it3mh4pciq5i801",
    "answeredAt": "2025-06-06T22:42Z",
    "fromCurrentEmployee": true
  }
}
```

Interview questions live here. Indeed has no separate interviews dataset — `/cmp/{slug}/interviews` shows the company profile, and interview content is published as Q\&A.

</details>

### How many results can I scrape?

Indeed limits how deep any single search can go. Paging alone reaches roughly **700-1,000
distinct postings per search**, after which Indeed recycles results rather than serving new
ones. Measured on a search Indeed reported as 2,676 postings:

| | Requests | Unique jobs | Of Indeed's count |
| :--- | ---: | ---: | ---: |
| Paging only | 41 | 737 | 27.5% |
| With `fanOut` on | 369 | 1,388 | 51.8% |

Widening is not free: paging returns about 18 new jobs per request, widening about 1.8,
because slices overlap on purpose and duplicates are dropped. That is why `fanOut` is a
switch with a budget rather than something the actor does behind your back. To go wider
still, add `deepSearch`, or split the search yourself by location or date and run it more
than once.

Every run reports what it actually collected, so a capped run is never presented as
complete:

```
Coverage [software engineer | fromage=14]: harvested 198 of 442 reported (44.8%)
Coverage (run): 1388 unique jobs of 2677 reported across all root queries — 51.8%
```

Indeed's own total is a live estimate — repeating one query minutes apart returns different
totals — so an occasional figure above 100% means jobs were posted while the run was in
flight, not that something is broken.

### How much does it cost?

This actor uses Apify's **pay-per-event** model: you are charged for the results it
produces, not for how long it runs.

| Event | Charged | Notes |
| :--- | :--- | :--- |
| `search-request` | Once per search page fetched | Billed **before** filtering, because the search costs the same to run either way. This is what keeps strict matching, one-job-per-company and only-new-jobs free to use. |
| `job-scraped` | Once per job saved | The main unit of value. The full description is included — it is fetched in batches, not per job. |
| `job-detail-scraped` | Once per job, only when `includeJobDetail` is on | Adds an extra fetch per job for benefits, qualifications and attributes. |
| `company-scraped` | Once per employer profile saved | |
| `contact-enriched` | Once per employer that actually yields a contact address | Employers whose site publishes no address are never billed for it. |
| `review-scraped` | Once per review saved | |
| `salary-scraped` | Once per salary row saved | |
| `qna-scraped` | Once per question saved | |

#### Controlling your spend

- **`maxItems`** caps how many jobs are saved, and therefore how many `job-scraped` events
  are charged. Default `100`.
- **`maxReviews`** and **`maxQuestions`** cap the per-employer review and Q\&A rows the same
  way.
- **Max total charge** (set per run or per task) is a hard ceiling. When it is reached the
  actor stops of its own accord and finishes with everything already saved.
- **Duplicates are never billed twice.** A job reached through several keywords, filters or
  overlapping searches is saved and charged once per run.
- **`onlyNewJobs`** stops you paying twice across runs: postings an earlier run already
  returned are skipped before any request is made.

### Supported countries

62 Indeed country domains, including the United States, United Kingdom, Canada, Australia,
Ireland, India, Germany, France, Spain, Italy, Netherlands, Poland, Sweden, Brazil, Mexico,
Japan, Singapore and South Africa. Set `country` to the two-letter code; currency, locale
and distance units follow.

### Using a signed-in session (optional)

Indeed keeps desktop search pagination behind a sign-in. Everything else this actor does
works signed out, which is the default — but if you want the desktop pages, paste cookies
from a browser you are already logged in with:

```json
{ "keywords": ["nurse"], "location": "Austin, TX",
  "sessionCookies": "CTK=…; SESSION_ID=…" }
```

Either the raw `Cookie:` header or a JSON array of `{name, value}` works, so a cookie
export extension's output can be pasted straight in.

- **No password is ever requested or handled.** You supply a session you already have.
- **Cookies go to indeed.com and nowhere else.** Employer-website enrichment fetches
  third-party domains, and those requests carry no Indeed session.
- **Values are never logged or written to the dataset** — the run log names the cookies it
  received and nothing more.
- **An expired session degrades, it does not fail.** Indeed answers a signed-out desktop
  request with a challenge rather than a readable sign-in page, so the actor detects the
  failure once, says so, and continues on the signed-out surface for the rest of the run.

### FAQ

**Does Indeed have an API I can use instead?**
Indeed's developer APIs are aimed at the hiring side — applicant tracking, conversions,
interview scheduling. The Publisher Jobs API that served job search was deprecated, which
is why a scraper is the practical route to this data.

**Do I need an Indeed account?**
No. Everything above runs signed out. A session is optional and only unlocks desktop
search pagination.

**Why do I get fewer jobs than Indeed's result count?**
Indeed will not page a single query past roughly 1,000 results. See
[How many results can I scrape?](#how-many-results-can-i-scrape) — the run log tells you
exactly what was collected.

**Can I get interview questions?**
Yes, in the Q\&A records. Indeed has no separate interviews dataset — `/cmp/{slug}/interviews`
renders the company profile, and interview content is published as community Q\&A, so that
tab is routed there.

**Can I filter by experience level?**
No. Indeed's experience-level URL parameter does not change the result set — measured
against a control — and search results carry no experience field to filter on afterwards.
Rather than ship a control that quietly does nothing, this actor does not have one. Use
`keywords` (`"senior data analyst"`) or `strictMatch` instead.

**Why is there no external apply URL?**
Indeed publishes only a tracking redirect for off-site applications, and that chain cannot
be resolved outside a real browser click — it returns 404 even on the session that issued
it. `apply.isIndeedApply` tells you which kind of application a posting uses.

**Can I run this on a schedule?**
Yes. Set `storeName` and `onlyNewJobs` and each run returns only postings earlier runs did
not, without spending requests on the ones it already sent you.

**Is it legal to scrape Indeed?**
This actor collects publicly available job listings and employer pages — the same pages any
visitor can open without signing in. Web scraping is legal in many jurisdictions when it is
limited to public data, but how you use what you collect is your responsibility, and
personal data carries obligations under the GDPR, the CCPA and their equivalents. If your
use case touches personal data or you are unsure, take legal advice.

### Not supported

- **Résumés** — `resumes.indeed.com` is not supported. This is a choice, not a technical
  limit: those pages carry job seekers' names, locations, employers, education and work
  history, and bulk-collecting identifiable personal data about private individuals into a
  redistributable dataset is a different activity from scraping employers' public job ads.
  Reachability is beside the point — the objection is to the collection, not to the
  difficulty.
- **Anything requiring a signed-in job seeker's own account** — saved jobs, messages,
  application status.

### Troubleshooting

- **Blocked or challenged runs** — use residential proxies. Indeed challenges most datacenter traffic. Lowering `maxConcurrency` also helps.
- **Contact emails mostly empty** — expected. Measured across 11 employers: 5 listed a website at all, and 1 of those 5 published an address. Indeed lists a website only for *claimed* employer profiles, and most corporate sites use a contact form instead of publishing an address.
- **`onlyNewJobs` returned nothing** — the search found nothing new since the last run. That is the feature working. Clear the store, change `storeName`, or lower `seenRetentionDays` to see repeats again.
- **A company was skipped with `includeCompanyDetails`** — employer slugs are derived from company names when a search result carries no company link, which can miss where Indeed disambiguated similar names.

### Feedback

Found a field that should be there, a filter that misbehaves, or a country that does not
resolve? Open an issue on the actor page — bug reports with the input JSON that produced
them get fixed fastest.

# Actor input Schema

## `scrapeMode` (type: `string`):

Which kind of work this run does. Jobs runs a keyword/location search. Company scrapes profiles, reviews, salaries and Q\&A from company URLs. Career salary scrapes aggregate pay by job title. Fields belonging to the other modes are ignored, and the log names anything skipped.

## `keywords` (type: `array`):

Job titles, skills or company names. Each keyword is searched separately.

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

City, state, ZIP or region. Leave empty to search the whole country.

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

Which Indeed country domain to search. Sets the domain, currency and language.

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

Any Indeed URL: a /jobs search (filters in the query string are honoured), a /viewjob job page, a /cmp company page or tab, or a /career salary page.

## `jobKeys` (type: `array`):

Look up specific jobs by Indeed job key — the jk value in a job URL, e.g. fb2c1853dfef615a.

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

Only return jobs posted within this window.

## `jobType` (type: `array`):

Filter by employment type, using Indeed's own list. Indeed accepts one type per search, so selecting several runs one search per type — which widens coverage rather than narrowing it.

## `remoteWork` (type: `string`):

Filter to remote or hybrid positions. Indeed filters remote server-side; it has no working hybrid filter, so hybrid is applied to the results instead and a hybrid run reads more pages for the same number of rows.

## `education` (type: `string`):

Filter by the education a job requires. Indeed supports this filter but few scrapers expose it.

## `salaryMin` (type: `integer`):

Filter to jobs at or above this annual salary, in the country's currency. Uses Indeed's own estimate-based pay filter.

## `onlyWithSalary` (type: `boolean`):

Drop jobs with no stated or estimated pay. Applied by Indeed, so it costs nothing.

## `onlyExternalJobs` (type: `boolean`):

Keep only jobs whose application happens on the employer's own site, not through Indeed Apply.

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

How far from the location to search, in miles. Only used when a location is set.

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

Unit for the search radius.

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

Relevance is Indeed's default ranking. Date returns the newest postings first.

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

Total jobs to return across all keywords and slices.

## `maxPages` (type: `integer`):

Indeed serves 25 jobs per page and starts recycling results past about page 40, so 50 is a practical ceiling.

## `fanOut` (type: `boolean`):

When a search reports more results than pagination can reach, re-run it as narrower slices (by date, job type and work arrangement) to reach more of the set.

## `maxFanOutRequests` (type: `integer`):

Caps how many extra searches widening may issue, so a broad query cannot run away.

## `deepSearch` (type: `boolean`):

Expand each keyword into related phrasings (seniority variants and title synonyms) to reach jobs the original wording never surfaces. Uses the widening budget.

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

Drop jobs already seen in this run. Slices overlap by design, so leaving this on is recommended.

## `strictMatch` (type: `boolean`):

Drop jobs whose title and description do not contain your keywords. Indeed pads every result page with loosely-related and sponsored jobs.

## `uniqueCompanies` (type: `boolean`):

Return at most one job per employer, so every row is a distinct company.

## `resumeKeywords` (type: `array`):

Skills from a CV, one per line. Every job is scored against them and gains resumeMatchScore (0-100) plus matched and missing skill lists. Not a search filter — it does not change which jobs Indeed returns.

## `storeName` (type: `string`):

Name a store to remember which jobs this search has already returned, so scheduled runs can skip them. Use a distinct name per saved search — for example "nurses-texas". Leave empty for one-off runs.

## `onlyNewJobs` (type: `boolean`):

Return only postings no earlier run has returned. Requires a memory store name. Skipped jobs cost no requests and no per-result charge, which is what makes a daily schedule affordable.

## `seenRetentionDays` (type: `integer`):

How long a job stays remembered. Older entries are pruned, so a long-lived store stays small and a re-posted job can surface again.

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

Fetched in batches of 40 jobs per request, so this is nearly free.

## `includeJobDetail` (type: `boolean`):

Adds benefits, requirements and attributes. Costs one extra request per job — slower and more expensive.

## `includeCompanyDetails` (type: `boolean`):

Adds industry, size, revenue, CEO and headquarters. Costs one extra request per unique company.

## `enrichEmails` (type: `boolean`):

Read each employer's own public website for a business contact address (careers@, hr@, info@ and similar), adding contactEmails and contactWebsite to the company record. Requires company details, since that is where the website comes from. Fetches at most three pages per employer and never leaves the employer's own domain.

## `qualifyByPayment` (type: `boolean`):

Requires the contact-email option. Reports which payment processors appear on the employer's site (Stripe, PayPal, Shopify and others) as a signal that the business transacts online. Reuses the pages already fetched, so it costs no extra requests.

## `companyUrls` (type: `array`):

Indeed company pages, e.g. https://www.indeed.com/cmp/Google

## `companyDataTypes` (type: `array`):

Which company data to collect. Profile and reviews are the default; salaries and Q\&A each cost extra requests. Indeed serves interview content inside Q\&A rather than as a separate dataset.

## `maxReviews` (type: `integer`):

Reviews are returned 20 per page. Set 0 for no limit.

## `maxQuestions` (type: `integer`):

Community questions are returned 10 per page. Set 0 for no limit.

## `reviewsSince` (type: `string`):

YYYY-MM-DD. Leave empty for all reviews.

## `salaryJobTitles` (type: `array`):

Used with the Career salary mode, e.g. "software engineer".

## `salaryLocations` (type: `array`):

Optional locations to pair with each salary job title, e.g. "New York, NY".

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

Residential proxies are strongly recommended — Indeed challenges most datacenter traffic.

## `sessionCookies` (type: `string`):

Optional. Paste cookies from a browser you are already signed in to Indeed with, either as the raw Cookie header (`CTK=…; SESSION_ID=…`) or as a JSON array of {name, value}. A signed-in run pages through the desktop site, which is closed to anonymous visitors. The actor never asks for your password, sends these cookies only to indeed.com, and never writes them to the log or the dataset. Leave empty to run signed out — that is the default and works for everything except desktop pagination.

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

Lower values are gentler and less likely to be challenged.

## `minConcurrency` (type: `integer`):

Minimum number of pages processed at the same time.

## `maxRequestRetries` (type: `integer`):

How many times a blocked or failed request is retried on a fresh proxy session before giving up.

## Actor input object example

```json
{
  "scrapeMode": "jobs",
  "keywords": [
    "software engineer"
  ],
  "location": "New York, NY",
  "country": "US",
  "startUrls": [],
  "jobKeys": [],
  "datePosted": "",
  "jobType": [],
  "remoteWork": "",
  "education": "",
  "onlyWithSalary": false,
  "onlyExternalJobs": false,
  "radius": 25,
  "radiusUnit": "miles",
  "sort": "relevance",
  "maxItems": 100,
  "maxPages": 50,
  "fanOut": true,
  "maxFanOutRequests": 50,
  "deepSearch": false,
  "deduplicate": true,
  "strictMatch": false,
  "uniqueCompanies": false,
  "resumeKeywords": [],
  "onlyNewJobs": false,
  "seenRetentionDays": 30,
  "includeDescription": true,
  "includeJobDetail": false,
  "includeCompanyDetails": false,
  "enrichEmails": false,
  "qualifyByPayment": false,
  "companyUrls": [],
  "companyDataTypes": [
    "profile",
    "reviews"
  ],
  "maxReviews": 100,
  "maxQuestions": 50,
  "salaryJobTitles": [],
  "salaryLocations": [],
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  },
  "maxConcurrency": 5,
  "minConcurrency": 1,
  "maxRequestRetries": 6
}
```

# Actor output Schema

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

Every record from the run. Use the view selector to switch between jobs, companies, reviews, salaries and Q\&A.

# 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 = {
    "scrapeMode": "jobs",
    "keywords": [
        "software engineer"
    ],
    "location": "New York, NY",
    "maxItems": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("autofacts/indeed-jobs-scraper-companies-reviews-salaries").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 = {
    "scrapeMode": "jobs",
    "keywords": ["software engineer"],
    "location": "New York, NY",
    "maxItems": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("autofacts/indeed-jobs-scraper-companies-reviews-salaries").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 '{
  "scrapeMode": "jobs",
  "keywords": [
    "software engineer"
  ],
  "location": "New York, NY",
  "maxItems": 100
}' |
apify call autofacts/indeed-jobs-scraper-companies-reviews-salaries --silent --output-dataset

```

## MCP server setup

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

```

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/hDKzEHDnKHWidEwXr/builds/YZQ92NkHlcuiLCogY/openapi.json
