# Indeed Jobs Scraper - Complete Results (`solidscripting/indeed-scraper`) Actor

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

## Pricing

from $0.75 / 1,000 results

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

Scrapes job listings from Indeed and returns **the number you asked for** — not a partial first page.

Ask for 1,000 jobs, get 1,000. Ask for more than exist and it tells you so, rather than handing back a short dataset and letting you assume that was everything.

***

### Why this one

Indeed caps any single search at roughly one page of results. Most scrapers accept that ceiling, which is why you see descriptions like *"returns over 1000 results on average — on a good day."*

This one works around it. A broad search is split into many narrower ones across the surrounding towns, each staying under the cap, and the results are merged and de-duplicated. Coverage comes from breadth, not from paging deeper into a wall.

Two things follow from that:

- **You get the count you requested**, assuming the jobs exist.
- **When they don't exist, you're told.** The run reports how many locations it searched and why it stopped, so a short result is a fact about the job market rather than a mystery about the scraper.

You are billed per row delivered — $0.75 per 1,000 jobs. A run that comes back empty costs you the $0.00005 it took to start it.

***

### Input

| Field | Description |
|---|---|
| `query` | Job title or keywords, e.g. `registered nurse` |
| `location` | City, or `City, ST` in the US and Canada |
| `queries` | Several job titles, searched in every location |
| `locations` | Several cities, each also expanded across nearby towns |
| `country` | Which Indeed site to search — 62, all measured |
| `maxItems` | How many jobs you want back. Default 50 |
| `postedWithinDays` | Only jobs posted in the last N days. Empty for all ages |
| `includeDescription` | Add the full description text. Off by default — read the note below |
| `radiusKm` | How far around the location to reach. Default 100, max 300 |
| `maxSubLocations` | How many nearby towns to search across. Default 120 |
| `strictRelevance` | Drop jobs that don't contain your search words. Off by default |

```json
{
  "query": "registered nurse",
  "location": "New York, NY",
  "country": "us",
  "maxItems": 1000
}
```

Leave `query` empty to get every job in an area. Leave `location` empty to search a whole country.

#### Several titles and cities in one run

Paste lists instead of orchestrating one run per city from your own code:

```json
{
  "queries": ["registered nurse", "nurse practitioner", "paramedic"],
  "locations": ["New York, NY", "Chicago, IL", "Houston, TX", "Phoenix, AZ"],
  "maxItems": 50000
}
```

Every title is searched in every city, and results are de-duplicated across all of them, so a job listed in two of your cities is delivered and billed once. Coverage is spread evenly rather than depth-first: the run works across your whole list at once, so if it stops at your target the rows come from every city you asked for, not from the first two.

Local names work — `München`, `Göteborg`, `Montréal`, `Warszawa` — as do `Austin, Texas` and `Toronto, Ontario` written out in full.

#### Running on a schedule: only what is new

Set `postedWithinDays` and you pay for fresh postings instead of re-buying the same jobs every morning:

```json
{
  "query": "warehouse associate",
  "locations": ["Dallas, TX", "Denver, CO", "Atlanta, GA"],
  "postedWithinDays": 1,
  "maxItems": 5000
}
```

Indeed applies the filter before it sends results, so this is not a run that fetches everything and throws most of it away — the search itself is smaller. Measured on one live search: `nurse` around New York holds 7,938 jobs in total and 194 posted in the last day.

It also reaches postings a broad search does not show you. An unfiltered search is ordered by relevance and stops before it gets to the newest jobs, so **40 of the 42 jobs returned for the last day were absent from the unfiltered results entirely**. Worth setting even for a one-off run if what you want is what is new.

***

### Output

**62 distinct fields across a run**, whichever of them Indeed published for each listing — typically 36 on a US job and as many as 48 on the richest, fewer where a country publishes less. Measured on a 4,451-job run across ten US metros. One row:

```json
{
  "position": "Registered Nurse - ICU",
  "company": "NewYork-Presbyterian",
  "location": "New York, NY 10032",
  "city": "New York",
  "state": "NY",
  "postalCode": "10032",
  "latitude": 40.7143,
  "longitude": -74.006,
  "salary": "$110,000 - $145,000 a year",
  "salaryMin": 110000,
  "salaryMax": 145000,
  "salaryPeriod": "YEARLY",
  "salaryCurrency": "USD",
  "salaryIsEstimated": false,
  "jobType": "Full-time",
  "remoteType": "REMOTE_HYBRID",
  "companyRating": 3.9,
  "companyReviewCount": 1203,
  "benefits": ["401(k)", "Health insurance", "Paid time off"],
  "urgentlyHiring": true,
  "employerResponseTimeHours": 6,
  "easyApply": true,
  "applyMethod": "indeed",
  "atsProvider": "Greenhouse",
  "requiredSkills": ["BLS Certification", "Registered Nurse (RN) License"],
  "preferredSkills": ["Critical care experience", "Bachelor's degree"],
  "skills": ["Acute care", "BLS Certification", "Care coordination", "Triage"],
  "hoursMin": 36,
  "hoursMax": 40,
  "hoursPeriod": "WEEKLY",
  "isSponsored": true,
  "sponsoredBidScore": 73218,
  "companyLogoUrl": "https://d2q79iu7y748jz.cloudfront.net/s/_squarelogo/...",
  "postedAt": "3 days ago",
  "postedDate": "2026-09-22T00:00:00.000Z",
  "url": "https://www.indeed.com/viewjob?jk=4798efd1d9b83baa"
}
```

What that gets you that a plain title-and-company dump does not:

**What the job actually asks for.** `requiredSkills` and `preferredSkills` carry the employer's own split between essential and nice-to-have, and `skills` carries every qualification Indeed has tagged the listing with — measured on 98% of rows, median 29 entries. Structured, so you can filter on it, rather than a paragraph of prose you have to parse.

**The employer's hiring system.** `atsProvider` names the applicant tracking system behind the listing — Greenhouse, Lever, Workday, iCIMS, SmartRecruiters, BambooHR, UKG, Paycom and twenty more. If you sell to recruiters, that is a qualified lead list falling out of a job scrape. Present on about a third of US rows, and far less outside the US — see the country table below before you buy it for that. Where the vendor is unrecognised you still get `atsHost`.

**Salary you can compute on.** `salaryMin` / `salaryMax` / `salaryPeriod` / `salaryCurrency` as numbers, plus `salaryIsEstimated` so you can tell Indeed's own estimates apart from figures the employer actually published. That distinction matters for benchmarking and nobody else surfaces it.

**Paid placements, and how hard they are bid.** `isSponsored` separates ads from organic results, and `sponsoredBidScore` carries Indeed's own auction value for the placement — zero on every organic row, non-zero on every sponsored one. The unit is Indeed's, not published anywhere, so treat it as relative: it tells you which employers are spending hardest on which roles.

**Location you can map.** City, state and postcode split into separate fields, with coordinates on 87% of rows measured across all 62 markets, plus the nearest transit station in metros that have one.

**Hiring signals.** `urgentlyHiring`, `hiresNeeded`, `employerResponseTimeHours`, `applyCount`, and exact weekly hours where they are stated — the fields that tell you which postings are live rather than stale.

**Non-US markets publish less salary, but not less detail.** Indeed publishes pay on most US listings and on few German or Swedish ones. The requirement fields fill that gap: a German run measured `skills` on 40 of 40 rows, in German, at 34 fields per row.

**Company context.** Rating, review count, logo, and links to the employer's Indeed profile.

Fields with no value are omitted rather than returned as empty strings, so your schema stays clean.

#### Full job descriptions

Every row carries `snippet`, which is Indeed's own preview and caps at about 200 characters. Set `includeDescription` and you get `description` as well — the complete text, measured at a median of **4,300 characters** across live jobs, with pay, benefits and requirements as the employer wrote them.

```json
{
  "query": "registered nurse",
  "location": "Brooklyn, NY",
  "includeDescription": true,
  "maxItems": 200
}
```

**The trade-off, stated plainly.** The description is not on the search page, so each job needs its own page fetched. A search request returns about 57 jobs; a description request returns one. Indeed limits how many requests it will answer, so a run with descriptions returns **hundreds of jobs rather than thousands** before it starts being refused. Keep `maxItems` modest, or run in batches.

If a job page can't be fetched, that row is still delivered with all its other fields and the run tells you how many arrived without a description. You are never billed for a row you didn't get.

***

#### What arrives in each country

**Every one of the 62 markets has been run and measured**, not assumed. Each returned its full 40 of 40 rows with a job title on every row. The numbers below are from one sweep on the same day with no search term, so they describe what a market publishes generally rather than what one profession publishes — and the differences are about employers, not about the scraper.

Local spellings work throughout, in any script: `München`, `Göteborg`, `Warszawa`, `São Paulo`, `東京`, `서울`, `กรุงเทพฯ`, `Αθήνα`, `القاهرة`, `תל אביב`.

**Americas**

| Country | Rows | Fields | Salary | Skills | Coords |
|---|---|---|---|---|---|
| United States | 40/40 | 35 | 98% | 98% | 83% |
| Canada | 40/40 | 37 | 95% | 98% | 88% |
| Mexico | 40/40 | 34 | 95% | 93% | 70% |
| Brazil | 40/40 | 31 | 75% | 83% | 100% |
| Argentina | 40/40 | 26 | 38% | 0% | 83% |
| Chile | 40/40 | 26 | 23% | 0% | 100% |
| Colombia | 40/40 | 25 | 28% | 0% | 88% |
| Peru | 40/40 | 30 | 90% | 0% | 100% |
| Venezuela | 40/40 | 24 | 0% | 0% | 83% |
| Ecuador | 40/40 | 19 | 23% | 0% | 100% |
| Costa Rica | 40/40 | 22 | 0% | 0% | 75% |
| Panama | 40/40 | 19 | 0% | 0% | 100% |
| Uruguay | 40/40 | 19 | 3% | 0% | 100% |

**Europe**

| Country | Rows | Fields | Salary | Skills | Coords |
|---|---|---|---|---|---|
| United Kingdom | 40/40 | 32 | 85% | 93% | 83% |
| Ireland | 40/40 | 37 | 78% | 68% | 95% |
| Germany | 40/40 | 33 | 55% | 70% | 100% |
| France | 40/40 | 29 | 70% | 85% | 95% |
| Netherlands | 40/40 | 25 | 35% | 70% | 60% |
| Belgium | 40/40 | 26 | 35% | 73% | 93% |
| Switzerland | 40/40 | 23 | 13% | 75% | 60% |
| Austria | 40/40 | 21 | 0% | 10% | 88% |
| Spain | 40/40 | 28 | 58% | 73% | 100% |
| Italy | 40/40 | 31 | 88% | 83% | 88% |
| Portugal | 40/40 | 30 | 50% | 33% | 90% |
| Sweden | 40/40 | 23 | 5% | 55% | 95% |
| Norway | 40/40 | 22 | 0% | 38% | 90% |
| Denmark | 40/40 | 22 | 0% | 43% | 83% |
| Finland | 40/40 | 20 | 0% | 0% | 88% |
| Poland | 40/40 | 23 | 15% | 75% | 100% |
| Czechia | 40/40 | 26 | 75% | 0% | 98% |
| Hungary | 40/40 | 21 | 5% | 0% | 95% |
| Romania | 40/40 | 18 | 0% | 0% | 95% |
| Greece | 40/40 | 21 | 0% | 0% | 95% |
| Ukraine | 40/40 | 19 | 10% | 0% | 93% |
| Turkey | 40/40 | 29 | 55% | 0% | 85% |
| Luxembourg | 40/40 | 20 | 8% | 0% | 30% |

**Asia-Pacific**

| Country | Rows | Fields | Salary | Skills | Coords |
|---|---|---|---|---|---|
| Australia | 40/40 | 24 | 20% | 78% | 30% |
| New Zealand | 40/40 | 22 | 0% | 5% | 98% |
| India | 40/40 | 33 | 93% | 90% | 100% |
| Singapore | 40/40 | 27 | 65% | 38% | 83% |
| Hong Kong | 40/40 | 20 | 13% | 63% | 70% |
| Malaysia | 40/40 | 30 | 88% | 0% | 85% |
| Philippines | 40/40 | 33 | 93% | 75% | 90% |
| Pakistan | 40/40 | 30 | 98% | 0% | 100% |
| Indonesia | 40/40 | 20 | 0% | 0% | 98% |
| Thailand | 40/40 | 19 | 18% | 0% | 90% |
| Vietnam | 40/40 | 18 | 8% | 0% | 85% |
| Japan | 40/40 | 30 | 58% | 65% | 100% |
| South Korea | 40/40 | 21 | 18% | 0% | 100% |
| Taiwan | 40/40 | 23 | 50% | 0% | 48% |
| China | 40/40 | 20 | 0% | 0% | 100% |

**Middle East & Africa**

| Country | Rows | Fields | Salary | Skills | Coords |
|---|---|---|---|---|---|
| South Africa | 40/40 | 23 | 33% | 0% | 70% |
| Nigeria | 40/40 | 30 | 98% | 0% | 90% |
| Egypt | 40/40 | 25 | 45% | 0% | 78% |
| Morocco | 40/40 | 19 | 0% | 0% | 98% |
| United Arab Emirates | 40/40 | 30 | 73% | 5% | 98% |
| Saudi Arabia | 40/40 | 28 | 48% | 0% | 100% |
| Qatar | 40/40 | 30 | 73% | 0% | 75% |
| Kuwait | 40/40 | 25 | 0% | 0% | 90% |
| Bahrain | 40/40 | 27 | 0% | 0% | 83% |
| Oman | 40/40 | 22 | 0% | 0% | 90% |
| Israel | 40/40 | 17 | 0% | 0% | 100% |

**Salary is the field that varies most, and it is entirely the market.** Employers in Pakistan, Nigeria, India and the US publish pay on nearly every listing. In Indonesia, New Zealand, Austria, Denmark, Norway, Finland, Greece and much of the Gulf they publish almost none. No scraper can return a figure the employer never posted; where salary is thin, `skills` and `requiredSkills` carry the detail instead.

**Coordinates average 87% and are weakest in Australia, Luxembourg and Taiwan** (around 30-48%), because those listings often name a suburb or district that is not a city in the place dataset. Where a job's own district is unknown, the surrounding city is used, so the point is city-level — see the note below.

**`atsProvider` is a US, UK, Canadian and Italian field.** It comes from the employer's apply integration: 58% in Italy, 10-15% in the US, UK and Canada, and zero or near-zero across most of Asia, Latin America and the Gulf. If the employer's hiring system is what you're buying, this is a Western dataset.

**Russia is not available.** Indeed withdrew from the country and `ru.indeed.com` returns a block page to everyone, so it has been removed from the list rather than left in to fail.

### Notes and limits

**Use a residential proxy.** Indeed refuses datacenter address ranges outright. The default configuration is correct; changing it to datacenter will return nothing. If you already have a residential provider, switch to Custom proxies and paste your own URLs — at volume that is usually cheaper per gigabyte than routing through the platform.

**Reach is set by locations, not pages.** Indeed serves about 55 jobs per search and refuses to page deeper, so coverage comes from searching more places. `maxSubLocations` and `radiusKm` are the levers; there is no deeper page to fetch.

**A single run realistically delivers a few thousand jobs, and the limit is the proxy, not the search.** Indeed rate-limits per address, so a large run works through addresses until the pool stops answering. Measured on ten US metros with one job title: 4,451 jobs in 20 minutes, from 721 searches of which 284 got through — the first 16 minutes had no outright failures at all, and then refusals became total within four. Runs needing up to roughly 280 searches (the 1,000 and 5,000-job runs) complete cleanly.

So if you want tens of thousands of jobs, **split the work across several runs** rather than asking one run for everything — by city, or by day using `postedWithinDays`. Bringing your own residential provider raises the ceiling a lot, because the platform pool is shared with everyone else scraping the same site. You are billed per row either way, so a run that stops short costs you only what it delivered.

**The run tells you its own ceiling at the start**, before spending anything, and says why it stopped at the end — so a short dataset is never a mystery.

**Locations are chosen for spread, not size.** Around New York the most populous places within reach are Brooklyn, Queens, Manhattan and the Bronx — one job market under several names, which would bill you repeatedly for the same listings. Towns are picked with a minimum separation between them instead, so a 40-town request reaches Bridgeport, Stamford, Edison, Trenton, Toms River and Danbury across three states. Where a radius is too tight to fill the count at that spacing, the spacing gives way rather than the count, so some near-neighbours do come back on dense requests.

**Indeed's matching is loose, and this returns what Indeed returns.** A search for `nurse` also brings back paramedics, EMTs and occasionally an electrician — that is Indeed's ranking, not a fault in the scraper. Set `strictRelevance` to drop anything that doesn't contain your search words. Be aware it will also drop `RN`, since the word "nurse" doesn't appear in it; abbreviated titles are the price of strictness, which is why it's off by default.

**Coordinates are city-level, and on 87% of rows.** Indeed does not publish street addresses, so `latitude` / `longitude` locate the city rather than the office - and where a listing names a district the place dataset does not hold, the surrounding city is used. Rows whose location cannot be placed at all carry no coordinates rather than a guess.

***

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

**$0.75 per 1,000 jobs**, plus $0.00005 to start a run. Nothing else — no compute charge, no proxy charge, no charge for the pages that get blocked and retried along the way.

| What you ask for | What you pay |
|---|---|
| 50 jobs (the default) | $0.04 |
| 1,000 jobs | $0.75 |
| 10,000 jobs | $7.50 |
| 100,000 jobs | $75.00 |

You are charged per row that lands in your dataset, so the run costs what the data costs and nothing more. If Indeed rate-limits every request and the run comes back empty, you pay the $0.00005 it took to start. If you ask for 5,000 jobs in a town that only holds 900, you pay for 900.

Duplicates are removed before anything is charged, so a job that appears in three of the cities you searched is delivered once and billed once.

The larger figures in that table are prices per row, not promises about a single run — see the note on proxy limits below. Tens of thousands of jobs is a job for several runs, and costs the same per row either way.

***

### Tips

**Ask for what you need, not the maximum.** The run stops the moment it reaches your target, and everything it has not fetched by then is free. A high `maxSubLocations` costs nothing on a small run.

**Use the lists for volume.** `queries` and `locations` reach much further than a bigger radius does, because a genuinely different city returns genuinely different jobs while a wider radius mostly returns the same ones.

**On a schedule, set `postedWithinDays`.** A daily run without it re-buys the whole job market every morning. With it you pay for the new postings and nothing else, and every row still carries `postedDate` as a real timestamp so you can filter or de-duplicate on your side.

**Leave `strictRelevance` off unless you have looked at the data.** It removes the off-topic rows, and it also removes `RN` from a nursing search. A junk row you can filter later; a real row that never arrives you will never notice.

**Bring your own proxies at volume.** Residential bandwidth is essentially the whole cost of running this. If you already pay a provider, switch to Custom proxies and paste your URLs.

***

### FAQ

**Is scraping Indeed legal?** This collects publicly visible job listings — no login, no personal data, nothing behind an account. Public data collection is broadly lawful in the EU and US, but you are responsible for what you do with the output, particularly under GDPR if you combine it with personal data. If you are unsure, take advice.

**Why did I get fewer jobs than I asked for?** Almost always because Indeed does not have that many for your search. The run says so explicitly in the log, along with how many locations it searched and why it stopped. Widening the radius or adding cities is what increases the number.

**Why does a search for one job title return others?** That is Indeed's own matching, not this scraper. A search for `nurse` returns paramedics and EMTs on Indeed's own website too. `strictRelevance` filters them out if you want that.

**Can I get the full job description?** Not currently. Indeed does not include the description in the search response, so it would need a separate request per job and a separate price. What you get instead is the structured version: `requiredSkills`, `preferredSkills` and `skills`, which are on almost every row and are easier to filter on than prose.

**Can I search several countries in one run?** No — one run covers one Indeed site. Run it once per country; you are billed per row either way, so it costs the same.

**Does it work outside the US?** Yes, and all 62 markets have been measured on live runs rather than assumed — see the country table above for exactly what arrives in each. You can type the local name of the city: `München`, `Göteborg`, `Montréal`, `Warszawa`. Expect fewer salary fields in many markets, because fewer employers there publish pay — in Indonesia, New Zealand, Austria and several Gulf states, almost none do.

***

### Support

Found a field that's empty when it shouldn't be, or a count that looks wrong? Open an issue. I use this myself and fix things quickly.

# Actor input Schema

## `query` (type: `string`):

What to search for, e.g. "python developer" or "registered nurse".

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

City, or "City, ST" in the US and Canada. Leave empty to search the whole country.

## `queries` (type: `array`):

Search several job titles in one run. Every title is searched in every location, results are deduplicated across all of them, and the run stops as soon as your target is met - so a long list costs nothing extra on a small run.

Use this instead of starting one run per title.

## `locations` (type: `array`):

Search several cities in one run - "City, ST" in the US and Canada, plain city names elsewhere. Each one is also expanded across nearby towns, and coverage is spread evenly across your list rather than exhausting the first city first.

Fifty cities in one run replaces fifty runs. If you also fill in the single Location field above, that city is searched as well.

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

Which Indeed site to search. 63 sites supported; thirteen are verified on live runs. What arrives varies by market rather than by the scraper - Polish employers publish salary on about 3% of listings against 85% in the Netherlands. See the README country table.

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

How many jobs you want back. The scraper searches across nearby towns automatically to reach larger numbers - Indeed caps a single search at about one page, and this works past that. Raise maxSubLocations and radiusKm for very large runs.

## `postedWithinDays` (type: `integer`):

Leave empty for all jobs regardless of age. Set it to 1 for jobs posted since yesterday, 7 for the last week, and so on.

This is the setting to use if you run this scraper on a schedule. Indeed applies it before sending results, so a daily run asks for a much smaller set of jobs and you are billed only for those - on one live search, 'nurse' around New York held 7,938 jobs in total but only 194 posted in the last day.

It also surfaces jobs a broad search hides. An unfiltered search is ordered by relevance and cuts off before it reaches the newest postings, so 40 of the 42 jobs returned for the last day were not in the unfiltered results at all.

Expect far fewer results than an unfiltered run - that is the point of it, not a shortfall.

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

Off by default. Adds the complete job description text to every row - typically around 3,500 characters, against the 200-character 'snippet' you get otherwise.

Read this before turning it on. The description is not on the search page, so each job needs its own page fetched: one request per job, instead of one request per fifty jobs. Indeed limits how many requests it will answer, so a run with descriptions returns far fewer jobs before it starts being refused - expect hundreds rather than thousands.

Use it with a modest 'Maximum jobs to return'. If you want volume, leave this off and use the 'snippet' field, or run it in batches.

## `radiusKm` (type: `integer`):

How far around the location to expand when splitting the search across nearby towns. Location is the axis that actually partitions Indeed results, so a wider radius is the main way to reach larger result counts.

## `maxSubLocations` (type: `integer`):

How many nearby towns to search across. Indeed serves about 55 jobs per search and refuses to page deeper, so this is what sets your reach. Towns are chosen for geographic spread rather than size, so you get distinct job markets instead of four names for the same city. Searching stops as soon as your target is met, so a high number costs nothing on small runs.

## `strictRelevance` (type: `boolean`):

Off by default, which returns exactly what Indeed returns.

Indeed's matching is loose: a search for "nurse" around New York also returns paramedics, EMTs and occasionally an electrician. Turning this on drops any job whose title and description do not contain your search words.

Worth knowing before you turn it on: it will also drop "RN", because the word "nurse" does not appear in it. Abbreviated titles are the cost of strictness.

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

Residential proxies are required. Indeed serves datacenter address ranges a hard 403 - including Apify's automatic proxy - so a run without residential returns nothing.

Running at high volume and already have a residential provider? Switch to Custom proxies and paste your own URLs. The scraper uses them instead, which usually works out cheaper per gigabyte than routing through the platform.

## Actor input object example

```json
{
  "query": "nurse",
  "location": "New York, NY",
  "queries": [],
  "locations": [],
  "country": "us",
  "maxItems": 50,
  "radiusKm": 100,
  "maxSubLocations": 120,
  "strictRelevance": false,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

All jobs found, one item each. Every row carries the fields Indeed published for that listing; fields with no value are omitted rather than returned empty.

## `jobsCsv` (type: `string`):

The same results as a spreadsheet-ready CSV.

# 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 = {
    "query": "nurse",
    "location": "New York, NY",
    "queries": [],
    "locations": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("solidscripting/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 = {
    "query": "nurse",
    "location": "New York, NY",
    "queries": [],
    "locations": [],
}

# Run the Actor and wait for it to finish
run = client.actor("solidscripting/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 '{
  "query": "nurse",
  "location": "New York, NY",
  "queries": [],
  "locations": []
}' |
apify call solidscripting/indeed-scraper --silent --output-dataset

```

## MCP server setup

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