# Indeed Scraper — Indeed Jobs API with Salaries | $0.10/1k (`glasswing/indeed-jobs-scraper`) Actor

Scrape Indeed jobs in 22 countries by keyword, location or Indeed URL: title, company, rating, size, salary parsed to min and max, job type, remote flag, posting date, full description and apply link. No login, proxy included. Export CSV, JSON or Excel.

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

## Pricing

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

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

### What does Indeed Jobs Scraper do?

Get Indeed job postings as clean, analysis-ready rows: title, company, company rating and size, city and state, the pay range parsed into `salaryMin` and `salaryMax`, job type, remote or hybrid flag, posting date, the **full job description** and the employer's own apply link. It is built for recruiters, job boards, salary analysts and sales teams who need Indeed listings in bulk instead of one browser tab at a time.

Type a keyword and a location - or paste an Indeed search URL - and **scrape Indeed jobs** into JSON, CSV or Excel, or read them straight from the Apify API. It works as a practical **Indeed API alternative** and **Indeed jobs API** for 22 Indeed country sites (indeed.com, uk.indeed.com, de.indeed.com, ca.indeed.com and more). Every row already carries the complete description, so there is no second "details" pass to pay for. The default 20-job run finished in **about 9 seconds**.

It reads only job data that Indeed serves publicly to any visitor of its job search. It does **not** log in, does not touch resumes or candidate profiles, and has no recruiter, email or phone-number columns. If an employer pasted an email address, phone number, personal profile link or a contact person's name into the posting text, it is replaced with `[email removed]`, `[phone removed]`, `[profile link removed]` or `[contact removed]` before the row is saved, in both `description` and `descriptionHtml`.

### Use cases for this Indeed scraper

- **Recruiting or staffing agency building a client pipeline.** Pull every new nurse, warehouse or software opening in a metro area each morning with `datePosted: "1"`, sort by `postedAt`, and reach the hiring company first. `companySize` and `companyIndustry` tell you whether the account is worth a call.
- **Compensation analyst tracking pay.** `salaryMin`, `salaryMax`, `salaryPeriod` and `salaryCurrency` come as numbers, and `salaryIsEstimate` separates the employer's own range from Indeed's estimate, so you can build hourly-rate and annual-band datasets per city, job type and country.
- **Job board or aggregator seeding listings.** Keep a feed fresh with title, company, logo, location, salary, job type, full `description` and a direct `applyUrl` to the employer's ATS (Workday, Greenhouse, Lever and others).
- **Sales team finding companies that are hiring.** A company posting ten SQL and Tableau roles is buying a data stack. Filter by `tags`, `company` and `companyRevenue`, then push the accounts into your CRM as hiring-intent signals.
- **Labour-market research.** Measure demand for a skill over time, or compare remote versus on-site volumes by state or country with `workplaceType`, `state` and `country`.
- **AI agents and RAG pipelines.** Every row is self-describing, the description is plain text, and the Actor can be called with no input at all.

### What data you get from every Indeed job

One row per job posting. Every column below exists in the dataset schema; the **Overview** table view shows `title`, `company`, `location`, `salaryText`, `jobTypes`, `workplaceType`, `postedAt`, `companyRating`, `url`, `applyUrl`, `status` and `error`.

**Row status and provenance**

| Field | Type | Description |
|---|---|---|
| `url` | string | Indeed job page (`https://www.indeed.com/viewjob?jk=...`, or the matching country site). For `not_found` and `error` rows, the Indeed API request that failed; its `f_q` (keyword) and `f_l` (location) parameters name the search. |
| `status` | string | `ok`, `not_found` or `error` (see the table below). |
| `error` | string | Reason when `status` is not `ok`. Absent on `ok` rows. |
| `scrapedAt` | string | ISO 8601 time of extraction. |
| `searchQuery` | string | Keyword of the search that found the job. |
| `searchLocation` | string | Location of that search. |
| `searchUrl` | string | The Indeed URL from your input that found the job, when you gave one. |
| `source` | string | Where Indeed got the job: the employer itself, its applicant-tracking system or another job board. |

**Job**

| Field | Type | Description |
|---|---|---|
| `jobKey` | string | Indeed's 16-character job key (the `jk` in job URLs). Stable for the life of the posting, so use it to deduplicate across runs. |
| `title` | string | Job title as published. |
| `jobTypes` | array | `full-time`, `part-time`, `contract`, `temporary`, `internship`, `permanent`, `fixed-term`, `freelance` or `per-diem`, when Indeed tags them. |
| `remote` | boolean | True when Indeed tags the job as remote. |
| `workplaceType` | string | `remote` or `hybrid`, when Indeed tags it. |
| `workSchedule` | string | Schedule text, e.g. `Monday to Friday`, when published. |
| `tags` | array | Skills, education, benefits and shift tags Indeed attached, e.g. `SQL`, `401(k) matching`, `Day shift`. |
| `applyUrl` | string | The employer's own apply page (its careers site or ATS), when the job links one. Absent for Indeed-only "Easily apply" jobs. |

**Company**

| Field | Type | Description |
|---|---|---|
| `company` | string | Hiring company name. |
| `companyRating` | number | Company's overall Indeed review rating, 1 to 5. |
| `companyReviewCount` | integer | Number of Indeed company reviews behind that rating. |
| `companyUrl` | string | Company page on Indeed. |
| `companyLogo` | string | Company logo image URL. |
| `companyWebsite` | string | Company's own website or careers page. |
| `companyIndustry` | string | Industry, e.g. `Retail`, `Healthcare`. |
| `companySize` | string | Employee band as Indeed shows it, e.g. `10,000+`. |
| `companyRevenue` | string | Revenue band as Indeed shows it, e.g. `$1B to $5B (USD)`. |
| `companyDescription` | string | Short company description from its Indeed profile. |

**Location**

| Field | Type | Description |
|---|---|---|
| `location` | string | Location as Indeed formats it, e.g. `Virginia Beach, VA 23462`. |
| `city` | string | City. |
| `state` | string | State or region code, e.g. `NY`. |
| `postalCode` | string | Postal code, when published. |
| `streetAddress` | string | Street address of the workplace, when the employer published one. |
| `country` | string | ISO country code of the job, e.g. `US`, `GB`, `DE`. |
| `countryName` | string | Country name. |

**Pay**

| Field | Type | Description |
|---|---|---|
| `salaryText` | string | The pay range as one readable string, e.g. `USD 61,000 - 97,000 per year`. |
| `salaryMin` | number | Lowest pay of the range. |
| `salaryMax` | number | Highest pay of the range (equal to `salaryMin` for a single figure). |
| `salaryPeriod` | string | `hour`, `day`, `week`, `month` or `year`. |
| `salaryCurrency` | string | ISO 4217 currency code, e.g. `USD`, `GBP`, `EUR`. |
| `salaryIsEstimate` | boolean | `false` when the employer published the pay, `true` when it is Indeed's own estimate. |

**Dates**

| Field | Type | Description |
|---|---|---|
| `postedAt` | string | ISO 8601 time the job was published. |
| `postedDaysAgo` | integer | Whole days between `postedAt` and the run. |
| `firstSeenAt` | string | ISO 8601 time the job first appeared on Indeed (can be later than `postedAt` for jobs Indeed picked up from another site). |

**Posting text**

| Field | Type | Description |
|---|---|---|
| `snippet` | string | The first ~300 characters of the description, for previews. |
| `description` | string | The full description as plain text with line breaks, contact details removed. On every `ok` row. |
| `descriptionHtml` | string | The full description as HTML, contact details removed. Only with **Include description HTML** on. |

A field is simply omitted when Indeed does not publish it. That is honest, not a guess. In the default run all 20 jobs had a description, city and posting date, 18 had a salary, 13 a company rating and 3 an industry; the share varies by country and search (German postings, for example, carry a salary much less often).

#### Result status (tri-state output)

| `status` | Meaning | Billed? |
|---|---|---|
| `ok` | The job was found and extracted. | Yes |
| `not_found` | Indeed answered, but the search has no matches or the job key no longer exists. | No |
| `error` | Indeed could not be read after retries (network error, block, or a change on Indeed's side). `error` says why. | No |

### How to scrape Indeed with this Actor

1. Open the Actor in Apify Console and click **Try for free**.
2. Type one or more **Keywords** (one search per line), a **Location** and pick the **Country (Indeed site)**. Or paste Indeed search URLs from your browser into **Indeed URLs** - they keep their own keyword, location and filters.
3. Optionally set **Date posted**, **Job type** and **Remote / hybrid**.
4. Set **Maximum jobs** (start with the default 20 to see the output).
5. Click **Start**. The default input takes about 10 seconds; 1,000 jobs take about a minute.
6. Open the **Output** tab or **Export** the dataset as JSON, CSV, Excel, XML or HTML.

To automate it, use the **API** tab (Node.js, Python, curl examples) or add a **Schedule**.

### Input

Every field is optional - the prefilled values already produce a complete run. See the **Input** tab for the full schema.

| Field | Type | Default | When to change it |
|---|---|---|---|
| `keywords` | array of strings | `["software engineer"]` | The job titles or skills you want, one search per line, e.g. `registered nurse`, `forklift driver`. When you give your own Indeed URLs, the example keyword `software engineer` is skipped. |
| `location` | string | empty (prefilled `New York, NY` in Console) | City, state, region or postcode as you would type it on Indeed (`London`, `Berlin`, `Texas`, `Remote`). Empty searches the whole country. |
| `country` | string | `us` | The Indeed site: `us`, `uk`, `ca`, `au`, `de`, `fr`, `in`, `nl`, `ie`, `es`, `it`, `ch`, `at`, `be`, `pl`, `mx`, `br`, `sg`, `nz`, `ae`, `za`, `jp`. The location must be in that country. |
| `radius` | integer | `25` | Distance around the location, 0 to 100: miles on the US and UK sites, kilometres elsewhere. |
| `datePosted` | string | `any` | `1`, `3`, `7` or `14` days. Use `1` on a daily schedule so you only pay for fresh postings. |
| `jobType` | string | `any` | `fulltime`, `parttime`, `contract`, `temporary`, `internship` or `permanent`. |
| `remote` | string | `any` | `remote` or `hybrid` for jobs Indeed tags that way. |
| `maxItems` | integer | `20` | Your main spending cap. With several keywords or URLs it is split evenly between them. |
| `includeDescriptionHtml` | boolean | `false` | Turn on to also get `descriptionHtml`. The plain-text `description` is always included. |
| `startUrls` | array of strings | one example search URL | Indeed search URLs (`https://www.indeed.com/jobs?q=nurse&l=Chicago`, also `uk.indeed.com`, `de.indeed.com` and the other sites), job URLs (`https://www.indeed.com/viewjob?jk=...`) or bare 16-character job keys. A job URL or key fetches exactly that posting; one that no longer exists gives a free `not_found` row. A search URL needs a `q` or `l` parameter; a malformed job key or any other page (home page, company page) gives one free `error` row and is never turned into a search. Results come in Indeed's relevance order; a `sort=date` parameter in a URL is ignored. When you type your own keywords, the built-in example URL is skipped. |

There is no proxy field: the Actor always uses an Apify residential proxy in the country of the Indeed site you search. It is included in the price.

Example input (exactly the prefilled default):

```json
{
    "keywords": ["software engineer"],
    "location": "New York, NY",
    "country": "us",
    "maxItems": 20,
    "startUrls": ["https://www.indeed.com/jobs?q=data+analyst&l=Remote"]
}
```

A daily UK feed of fresh remote contract roles:

```json
{
    "keywords": ["data engineer", "python developer"],
    "location": "London",
    "country": "uk",
    "datePosted": "1",
    "jobType": "contract",
    "maxItems": 500,
    "startUrls": []
}
```

### Output

You can download the dataset in various formats such as JSON, HTML, CSV or Excel. A real row from the default run (2026-10-16; `description` and `tags` shortened here), followed by what a search without matches returns:

```json
[
    {
        "url": "https://www.indeed.com/viewjob?jk=91179c3a512855a5",
        "status": "ok",
        "scrapedAt": "2026-10-16T14:59:00.013Z",
        "jobKey": "91179c3a512855a5",
        "title": "Lead Healthcare Data Analyst Remote",
        "company": "Sentara",
        "companyRating": 3.7,
        "companyReviewCount": 3152,
        "companyUrl": "https://www.indeed.com/cmp/Sentara-9",
        "companyLogo": "https://d2q79iu7y748jz.cloudfront.net/s/_squarelogo/256x256/92d0cb2bc67aac490daa4b1c29bf7cb3",
        "companyWebsite": "https://www.sentaracareers.com",
        "companySize": "10,000+",
        "companyRevenue": "$1B to $5B (USD)",
        "companyDescription": "Sentara is focused on living its mission – to improve health every day.",
        "location": "Virginia Beach, VA 23462",
        "city": "Virginia Beach",
        "state": "VA",
        "postalCode": "23462",
        "streetAddress": "350 Centre Pointe Dr",
        "country": "US",
        "countryName": "United States",
        "salaryText": "USD 122,408 - 179,649.59 per year",
        "salaryMin": 122408,
        "salaryMax": 179649.59375,
        "salaryPeriod": "year",
        "salaryCurrency": "USD",
        "salaryIsEstimate": false,
        "jobTypes": ["full-time"],
        "remote": true,
        "workplaceType": "remote",
        "tags": ["SQL", "Tableau", "Statistics", "Bachelor's degree", "401(k) matching", "Paid parental leave", "Day shift"],
        "postedAt": "2026-10-14T05:00:00.000Z",
        "postedDaysAgo": 2,
        "firstSeenAt": "2026-10-14T15:07:55.391Z",
        "snippet": "City/State Virginia Beach, VA Work Shift First (Days) Overview: Sentara is hiring for a Lead Healthcare Data Analyst! Overview The Healthcare Data Analyst Lead provides data analysis support to the customer by assisting with the development of reports and/or dashboards to monitor program and...",
        "description": "City/State\nVirginia Beach, VA\n\nWork Shift\nFirst (Days)\n\nOverview:\n\nSentara is hiring for a Lead Healthcare Data Analyst! ...",
        "applyUrl": "https://sentara.wd1.myworkdayjobs.com/SCS/job/Sentara-Centre-Pointe/Lead-Healthcare-Data-Analyst-Remote_JR-107759-1",
        "source": "Sentara Health",
        "searchQuery": "data analyst",
        "searchLocation": "Remote",
        "searchUrl": "https://www.indeed.com/jobs?q=data+analyst&l=Remote"
    },
    {
        "url": "https://apis.indeed.com/graphql?f_kind=search&f_co=US&f_q=zzqxwv+plorbnik&f_l=Chicago%2C+IL&f_s=RELEVANCE&f_max=5&f_i=0&f_p=0&f_n=0",
        "status": "not_found",
        "error": "No results for this page",
        "scrapedAt": "2026-10-16T14:59:01.120Z"
    }
]
```

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

This Actor uses **pay-per-event** pricing. The residential proxy and the platform compute are included:

| Event | Price | Charged when |
|---|---|---|
| Actor start | $0.005 per run | Once when the run starts. The platform charges one event per GB of memory and the Actor runs on 1 GB, so this is one event. |
| Job | $0.0001 per job ($0.10 per 1,000) | One job with `status: ok` was saved to the dataset, full description included. |

**1,000 Indeed jobs cost about $0.105** ($0.005 to start plus 1,000 x $0.0001). There is no extra charge for descriptions, company data or the HTML option. You are never charged for a row with `status` `not_found` or `error`. Overlapping searches (say `nurse` and `registered nurse` in the same city) can both find the same job; it is then saved once per search, so keep keywords distinct or deduplicate on `jobKey`. The free Apify plan's $5 of monthly credit covers tens of thousands of jobs.

You can cap spending per run with **Maximum jobs** and with the run's **Max total charge** option; the Actor stops gracefully when either limit is reached and keeps everything it has already saved.

### How it compares

This Actor reads the same job-search API that Indeed's own mobile app uses, which is why one request returns up to 100 jobs with full descriptions, and why runs take seconds instead of the minutes a browser-based Indeed scraper needs. Indeed's website shows a security check to automated page requests; the app API does not need a browser at all. If you want flat, typed columns (numbers for pay, ISO dates, booleans for remote), company firmographics on every row and a tri-state status you can trust inside an automated pipeline, this is a good fit.

It is not for you if you need resumes, candidate profiles or anything inside an Indeed account; employer contact names, emails or phone numbers (this Actor removes them on purpose); a vendor-backed feed with a stability guarantee; or one Actor that also covers other job boards (see **Related Actors** below).

### Integrations, exports and API access

Results export to **CSV, JSON, Excel**, XML and HTML from the **Output** tab or the dataset API. From there, Apify integrations and webhooks push the same rows into **Google Sheets, Airtable or a CRM**, plus Slack, Amazon S3 or your own endpoint - directly, or through Zapier, Make and n8n. Add a **Schedule** with `datePosted: "1"` and today's new postings land in your sheet every morning.

The Actor can also be called from the **Apify API** (Node.js, Python and curl snippets are on the **API** tab), from the **Apify MCP server**, or by any AI agent that can call a tool: the inputs are plain strings and numbers, and every row carries `status` and `error`, so an agent can tell an empty search apart from a failure without parsing prose.

### Tips

- Keep `maxItems` small while testing, then raise it.
- Put several keywords in one run instead of many single-keyword runs; you pay the start fee once. `maxItems` is split evenly across them.
- To cover a large market, split it into narrower searches (per city, per keyword, per job type) rather than one very deep search.
- Use `jobKey` to deduplicate rows across scheduled runs.
- With **Date posted** plus **Job type** or **Remote / hybrid** together, Indeed filters by date and the Actor checks the job type itself, so such a run may read a few more pages to fill `maxItems`. That costs you nothing extra: only saved jobs are billed.

### Limitations

- **Undocumented data source.** The Actor reads the public API of Indeed's mobile app, the same one its iPhone app calls. Indeed can change it at any time; when that happens rows come back as `error` (never silently wrong data) and the Actor is updated. Report it in the **Issues** tab.
- **Country coverage.** All 22 Indeed sites in the **Country** list use the same API. The US, UK and German sites are tested on every release; for the others, check the first rows of a small run.
- **Depth per search.** One search returns at most 2,000 jobs (20 pages of 100). Indeed itself rarely shows more than about 1,000 results for one query, so split large markets into narrower searches.
- **No newest-first sort.** Indeed's app API accepts a date sort but does not apply it (tested 2026-10-16), so rows come in relevance order. For recent jobs use `datePosted` (`1`, `3`, `7`, `14`), which the API does apply, and sort the export by `postedAt` or `firstSeenAt`.
- **Salary is optional on Indeed.** Many postings carry no pay, and some carry only Indeed's estimate (`salaryIsEstimate: true`). Coverage is high in the US and UK and low in Germany.
- **Company fields depend on the company's Indeed profile.** Small employers often have no rating, size, revenue or industry.
- **No login and no personal data.** Postings behind an Indeed account, resumes, candidate profiles and recruiter contact details are out of scope by design.
- Results reflect what Indeed published at the time of the run.

### FAQ

**Does this Actor need an Indeed account, an API key or my own proxy?** No. You only need an Apify account. There is no login step, the Actor sends the public key that Indeed's mobile app ships to every user, and the residential proxy is set up and paid for by the Actor.

**Does it get the full job description?** Yes, on every `ok` row, as plain text in `description`. Turn on **Include description HTML** if you also want the original formatting in `descriptionHtml`.

**Which countries does it support?** 22 Indeed sites: United States, United Kingdom, Canada, Australia, Germany, France, India, Netherlands, Ireland, Spain, Italy, Switzerland, Austria, Belgium, Poland, Mexico, Brazil, Singapore, New Zealand, United Arab Emirates, South Africa and Japan. Pick one with `country`, or paste a search URL from that site.

**How do I get more results?** Raise `maxItems` first. Past about 1,000 jobs per search, split the market into several narrower searches (per city, per keyword, per job type) and run them together.

**What happens if Indeed changes something?** Rows come back with `status: error` and a reason instead of silently wrong data, so your pipeline can alert on it. You are not billed for `error` rows. Report it in the **Issues** tab with the run ID.

**Is it legal to scrape Indeed?** Scraping publicly available data is generally legal, but you are responsible for how you use the output. Read the legal notice below and Indeed's terms of service, and do not collect personal data without a legitimate reason.

**Why did I get fewer rows than `maxItems`?** The search had fewer matches, or the run hit your **Max total charge** limit. Check the run log for "Stopping gracefully".

**Can I use this Actor from an AI agent or MCP client?** Yes. It runs with no input at all, every row is self-describing (`status` plus `error`), inputs are plain strings, and results can be fetched via the dataset API or the Apify MCP server.

### Related Actors

- [LinkedIn Jobs Scraper](https://apify.com/glasswing/linkedin-jobs-scraper) - use it for the same searches on LinkedIn, with seniority and applicant counts.
- [ATS Jobs Scraper](https://apify.com/glasswing/ats-jobs-scraper) - use it to read every opening straight from a company's own applicant-tracking system.
- [Dice Jobs Scraper](https://apify.com/glasswing/dice-jobs-scraper) - use it for US tech roles with skills and a visa-sponsorship flag.
- [Seek Jobs Scraper](https://apify.com/glasswing/seek-jobs-scraper) - use it for Australia and New Zealand, where Seek is the leading board.
- [XING Jobs Scraper](https://apify.com/glasswing/xing-jobs-scraper) - use it for Germany, Austria and Switzerland beyond what Indeed lists.
- [JobStreet Scraper](https://apify.com/glasswing/jobstreet-scraper) - use it for Southeast Asia (Malaysia, Singapore, Philippines, Indonesia).
- [Remote Jobs Scraper](https://apify.com/glasswing/remote-jobs-scraper) - use it for remote roles from seven remote-only job boards, merged and deduplicated.
- [Workable Jobs Scraper](https://apify.com/glasswing/workable-jobs-scraper) - use it for roles hosted on company Workable career pages.
- [Welcome to the Jungle Jobs](https://apify.com/glasswing/welcome-to-the-jungle-jobs) - use it for startup and tech roles in France and Europe.
- [Glassdoor Scraper](https://apify.com/glasswing/glassdoor-jobs-scraper) - use it for Glassdoor job listings with company ratings and salary estimates.
- [Google Jobs Scraper](https://apify.com/glasswing/google-jobs-scraper) - use it to collect job postings that Google aggregates from many job boards in one search.

### Legal and data-protection notice

This Actor extracts only data that Indeed publishes publicly; it does not extract private user data such as recruiter names, email addresses, phone numbers, gender or precise location of individuals (contact details found in free-text job descriptions are removed before the row is saved), and it does not log in or work around access controls. However, your results could still contain personal data. Personal data is protected by the GDPR in the European Union and by other regulations around the world. You should not scrape personal data unless you have a legitimate reason to do so. If you are unsure whether your reason is legitimate, consult your lawyers. You are responsible for complying with Indeed's terms of service and applicable law when using the extracted data.

This Actor is an independent tool and is not affiliated with, endorsed by or sponsored by Indeed, Inc. or its owners. All trademarks belong to their respective owners.

# Changelog

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

# Actor input Schema

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

Job titles or skills to search, one per line, as you would type them into Indeed's 'What' box (for example `nurse`, `python developer`). Each line is one search. The example `software engineer` is skipped when you give your own Indeed URLs.

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

City, state, region or postcode, as you would type it into Indeed's 'Where' box (for example `New York, NY`, `London`, `Berlin`, `Remote`). Leave empty to search the whole country.

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

Which Indeed country site to search. Location must be in this country.

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

Distance around the location: miles on the US and UK sites, kilometres elsewhere. Default 25.

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

Only jobs first posted on Indeed within this many days.

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

Only jobs Indeed tags with this job type.

## `remote` (type: `string`):

Only remote jobs, or only hybrid jobs, as tagged by Indeed.

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

Stop after this many jobs in total. With several keywords or URLs it is split evenly between them. Each job saved with status `ok` is one billable result.

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

Also save the job description as HTML (`descriptionHtml`). The plain-text `description` is always included.

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

Indeed search result URLs (for example `https://www.indeed.com/jobs?q=nurse&l=Chicago`) or job URLs (`https://www.indeed.com/viewjob?jk=...`), or bare 16-character job keys. Search URLs keep their own keyword, location and filters. Clear the example if you only want your keywords.

## Actor input object example

```json
{
  "keywords": [
    "software engineer"
  ],
  "location": "New York, NY",
  "country": "us",
  "datePosted": "any",
  "jobType": "any",
  "remote": "any",
  "maxItems": 20,
  "includeDescriptionHtml": false,
  "startUrls": [
    "https://www.indeed.com/jobs?q=data+analyst&l=Remote"
  ]
}
```

# Actor output Schema

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

No description

# 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 = {
    "keywords": [
        "software engineer"
    ],
    "location": "New York, NY",
    "maxItems": 20,
    "startUrls": [
        "https://www.indeed.com/jobs?q=data+analyst&l=Remote"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("glasswing/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 = {
    "keywords": ["software engineer"],
    "location": "New York, NY",
    "maxItems": 20,
    "startUrls": ["https://www.indeed.com/jobs?q=data+analyst&l=Remote"],
}

# Run the Actor and wait for it to finish
run = client.actor("glasswing/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 '{
  "keywords": [
    "software engineer"
  ],
  "location": "New York, NY",
  "maxItems": 20,
  "startUrls": [
    "https://www.indeed.com/jobs?q=data+analyst&l=Remote"
  ]
}' |
apify call glasswing/indeed-jobs-scraper --silent --output-dataset

```

## MCP server setup

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