# Indeed Ai Scraper (`hamas/indeed-ai-scraper`) Actor

Scrape Indeed job listings with titles, companies, salaries, locations, apply links, and more. Each job gets a free AI match score so you can focus on the most relevant roles, export straight to Google Sheets, and pay per request.

- **URL**: https://apify.com/hamas/indeed-ai-scraper.md
- **Developed by:** [Hamas jan](https://apify.com/hamas) (community)
- **Categories:** Jobs, Automation, Developer tools
- **Stats:** 4 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $1.67 / 1,000 request sents

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/platform/actors/running/actors-in-store#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

Extract structured job listings from Indeed — title, salary, company rating, remote status, benefits, and apply links — across **62 country domains**. Unlike every other Indeed scraper on Apify, this one includes **free AI-powered match scoring**: describe what you're looking for and every job gets ranked 0–100 for relevance, so you spend zero time manually reading through irrelevant postings.

### Why use this scraper?

- 🤖 **Free AI match scoring** — no other Indeed scraper on Apify does this. Set a minimum match score and only genuinely relevant jobs get saved. AI matching runs on our infrastructure and costs nothing extra — you only ever pay for scraping requests.
- 🌍 **62 countries supported** — from the US and UK to Japan, Brazil, UAE, and Nigeria — more country coverage than any comparable Indeed scraper.
- 💰 **Simple, predictable pricing** — pay per request, not per job or per month. Roughly $2 gets you ~1,000 jobs with default filters (see the cost breakdown below).
- 📊 **Google Sheets export with zero setup** — no API keys, no OAuth flow. Share your sheet with one email address and results land there automatically.
- 🎯 **Built-in filtering** — skip specific companies, skip jobs containing keywords you don't want (e.g. "clearance required," "unpaid"), cap results per company to keep your dataset diverse, and automatically de-duplicate and skip expired listings.
- 🔗 **Two ways to search** — type plain keywords and let the scraper build the Indeed search for you, or paste your own Indeed search URL (with Indeed's native salary/date/job-type filters already applied) and it takes priority automatically.
- 🔁 **Duplicate-safe across runs** — feed in job URLs from previous runs and the scraper will skip anything it already collected.

### How it works

You only need to fill in one of two things:

1. **Keywords** — enter job titles like `Python Developer` or `Sales Manager`, pick a country and location, and the scraper builds the Indeed search for you.
2. **Your own Indeed search URL** — if you've already set filters on Indeed (salary range, job type, date posted) that aren't exposed in the input form, copy that URL in instead. It overrides the keyword fields.

Set how many jobs you want, hit **Start**, and export as JSON, CSV, Excel, or straight to Google Sheets.

#### Input example — keyword search

```json
{
  "search_keywords": "Python Developer\nBackend Engineer",
  "search_country": "us",
  "search_location": "Austin, TX",
  "date_filter": "7",
  "max_jobs": 100,
  "ai_matching_enabled": true,
  "min_match_percentage": 60
}
```

#### Input example — your own Indeed URL

```json
{
  "start_urls": [
    "https://www.indeed.com/jobs?q=data+analyst&l=New+York%2C+NY&salaryType=%2480%2C000&fromage=7"
  ],
  "max_jobs": 200,
  "save_unique_only": true
}
```

### Why turn on AI matching (default on)

Without AI Matching, you get every job that matches your search terms on Indeed — the same broad, noisy result set you'd get browsing the site yourself. You'd still have to review each listing to determine whether it's actually relevant.

With AI Matching, every job is scored from 0–100 based on how well it matches your search keywords. Set min\_match\_percentage, and only jobs that meet or exceed that threshold are saved.

AI Matching is free — it runs on our side and never adds to your request count or bill. You only pay for the scraping requests themselves. Since irrelevant jobs are filtered out early, tighter matching can also result in a leaner, cheaper dataset overall.

### What's in each job record

| Field | Description |
|---|---|
| `id` | Indeed's unique job ID |
| `url` | Direct link to the job posting |
| `positionName` | Job title |
| `company` | Company name |
| `companyIndeedUrl` | Link to the company's Indeed profile |
| `location` | Job location |
| `salary` | Salary or pay range, if listed |
| `jobType` | Full-time, part-time, contract, etc. |
| `isRemote` | Remote / Hybrid / In-Person |
| `description` | Full job description (plain text) |
| `descriptionHTML` | Full job description (HTML) |
| `postedAt` | Relative post time (e.g. "3 days ago") |
| `postingDateParsed` | Exact posting date/time |
| `applyType` | "Easy Apply" (Indeed) or "CS Apply" (external site) |
| `externalApplyLink` | Direct apply link on the company's own site, when available |
| `benefits` | Listed benefits (health insurance, 401k, etc.) |
| `rating` | Company's overall Indeed rating |
| `reviewsCount` | Number of company reviews |
| `isExpired` | Whether the listing has expired |
| `jobMatch` | AI match score 0–100, when AI matching is enabled |
| `scrapedAt` | Timestamp of extraction |
| `searchInput` | The country, location, and keyword that found this job |

#### Sample output

```json
{
  "id": "4a925aff5a7e3ea6",
  "positionName": "Backend Engineer",
  "company": "Acme Robotics",
  "location": "Austin, TX",
  "salary": "$110,000 - $145,000 a year",
  "jobType": "Full-time",
  "isRemote": "Hybrid",
  "applyType": "CS Apply",
  "externalApplyLink": "https://careers.acmerobotics.com/job/4521",
  "rating": 4.1,
  "reviewsCount": 322,
  "isExpired": false,
  "jobMatch": 87,
  "postedAt": "2 days ago",
  "scrapedAt": "2026-08-10T09:14:00.000Z"
}
```

### Pricing — how much does it cost to scrape Indeed with this actor?

Billing is **pay per event** — a small flat fee per run, plus a charge for each request the scraper makes. No subscriptions, no minimum commitments.

| Event | Price | When it's charged |
|---|---|---|
| Actor run started | $0.005 | Once per run, regardless of size |
| Request sent | $0.0017 | Once for every search-results page **and** every individual job-detail page fetched |

You're billed **per request, not per saved job** — this is deliberate. Charging per result would hide the real cost of strict filtering; charging per request means the price you see is the work the scraper actually did.

| Spend | Approx. requests | Approx. jobs (default filters) |
|---|---|---|
| $2 | ~1,200 | ~1,000 |
| $10 | ~6,000 | ~5,000 |
| $50 | ~30,000 | ~25,000 |

**Why request count isn't the same as job count:**
The scraper only requests a job's detail page after that job has passed your filters at the search-results stage — but finding enough *passing* jobs can mean checking more candidates than you end up keeping. Concretely:

- A high `min_match_percentage` means more candidate jobs get scored and discarded before one clears the bar.
- A strict `per_company_jobs` cap means the scraper has to keep searching past jobs from companies that already hit their cap.
- A long `ignore_related` keyword list or `ignore_companies` list means more candidates get checked and rejected.

With default (loose) filters, the ratio stays close to **1.2 requests per saved job** — that's where the "$2 ≈ 1,000 jobs" estimate comes from. The tighter you filter, the more that ratio can rise, since you're paying for the search work, not just the keepers.

AI matching itself never adds requests or cost — scoring happens after a job is already fetched, so filtering by `min_match_percentage` alone doesn't multiply your request count the way search-side filters (company caps, keyword exclusions) can.

### Exporting to Google Sheets

1. Open your Google Sheet.
2. Click **Share** and add `python-api@indeed-leads-467810.iam.gserviceaccount.com` as an **Editor**.
3. Paste your sheet's URL into `google_sheet_url`.

No API keys, no OAuth screen, no service account setup on your end.

### Use cases

- **Job seekers** — pull every listing matching your target role and let AI match scoring surface the best fits first
- **Recruiters & staffing agencies** — track open roles across clients or competitors, filtered to exclude noise (staffing agency reposts, irrelevant seniority levels)
- **Market & salary research** — aggregate salary ranges by role, region, and company at scale across 62 countries
- **Lead generation** — identify actively hiring companies as warm outbound targets
- **Career coaches** — monitor postings for clients and filter out anything that doesn't match their criteria

### FAQ

**Is it legal to scrape Indeed?**
This scraper only extracts data that's publicly visible on Indeed's job listing pages — the same information any visitor sees without logging in. It doesn't access private data or bypass authentication. You're responsible for ensuring your specific use complies with Indeed's Terms of Service and any data-protection laws (GDPR, CCPA) that apply to your use case — if in doubt, consult a lawyer.

**How much does it cost to scrape 1,000 Indeed jobs?**
Around $2 with default filters, since pricing is pay-per-request (roughly 1,200 requests per 1,000 saved jobs). Stricter filters (high match score threshold, strict per-company caps) will use more requests per saved job.

**What countries does this scraper support?**
62 Indeed domains, including the US, UK, Canada, Australia, Germany, France, India, Japan, Brazil, UAE, Nigeria, and more. Set `search_country` or use `start_urls` with any country-specific Indeed domain.

**Does it get salary data?**
Yes — the `salary` field returns Indeed's listed or estimated pay range when the employer or Indeed provides one. Not every listing includes salary data; this depends on what Indeed itself has.

**What makes this different from other Indeed scrapers?**
Free AI match scoring is unique to this actor — every job is scored against your search keywords so you can filter by relevance, not just keyword presence. It also covers more countries (62) than comparable scrapers and includes no-auth Google Sheets export.

**Can I filter by remote jobs only?**
Yes, via the `isRemote` field in your downstream filtering, or by including "remote" in your search location/keywords so Indeed's own results are pre-filtered.

**How do I avoid re-scraping jobs I already have?**
Paste previously-collected job URLs into `processed_job_urls` — the scraper extracts the job IDs and skips anything already processed.

**Is there a free plan limit?**
Yes — free Apify plan users are capped at 100 jobs per run. Upgrade your Apify plan for higher volumes.

***

*Keywords: indeed scraper, indeed jobs scraper, indeed job api, scrape indeed, job listings scraper, salary data scraper, AI job matching, job market research, indeed salary data, remote jobs scraper*

# Actor input Schema

## `search_keywords` (type: `string`):

Enter job titles or keywords to search on Indeed, one per line. The scraper builds search URLs automatically from these.

Example:
Python Developer
Backend Engineer
Django Developer

Leave blank if you are providing your own 'Indeed Search URLs' below.

## `search_country` (type: `string`):

The country to search on Indeed. Determines which Indeed domain is used (e.g. indeed.com, uk.indeed.com).

## `search_location` (type: `string`):

City, state, or region to search within. Leave blank to search nationwide or for remote jobs.

Examples: San Francisco, CA | New York | Remote | London

## `date_filter` (type: `string`):

Only return jobs posted within this time window. Applied to keyword-based searches only — has no effect if you supply your own 'Indeed Search URLs'.

## `start_urls` (type: `array`):

Paste your own Indeed search URLs here. These take priority over the keyword/location/country fields above.

When to use this: if you want to use Indeed's filters (date posted, salary, job type) that aren't available through the keyword builder — set up your search on Indeed with those filters and copy the URL.

Leave blank to use the keyword search fields above.

## `processed_job_urls` (type: `string`):

Paste Indeed job URLs from previous runs to skip duplicates. The scraper extracts job IDs from these URLs and skips jobs that have already been processed.

## `ignore_companies` (type: `string`):

Jobs from these companies will be ignored. Enter one company name per line.

Example:
Google
Amazon
Staffing Solutions Inc

## `ignore_related` (type: `array`):

If a job description contains any of these words, that job is skipped (or flagged, depending on the 'Skip Jobs Matching Keywords' toggle). Enter one keyword per line.

Example:
clearance required
C++
management consulting

## `google_sheet_url` (type: `string`):

Export results to Google Sheets — no credentials needed.

Setup (one-time):

1. Open your Google Sheet.
2. Click Share.
3. Under 'General access', add this email with Editor access: python-api@indeed-leads-467810.iam.gserviceaccount.com
4. Paste the sheet's URL here.

## `min_match_percentage` (type: `integer`):

Only used when AI matching is enabled and search\_keywords is filled in. Jobs scoring below this threshold are skipped.

Lower = more results, less targeted. Higher = fewer but more relevant.
Recommended: 40–90.

## `max_jobs` (type: `integer`):

The scraper stops once it has saved this many jobs total across all keywords and search URLs.

## `per_company_jobs` (type: `integer`):

Limits how many jobs from the same company are saved. Keeps results diverse.

## `ai_matching_enabled` (type: `boolean`):

When enabled, the scraper computes a match score from 0–100 for each job showing how closely it matches your search\_keywords.

## `save_unique_only` (type: `boolean`):

When enabled, if the same job title at the same company appears more than once, for example across different search URLs, only the first occurrence is saved. Recommended to keep on.

## `skip_expired_jobs` (type: `boolean`):

When enabled, jobs marked as expired on Indeed are silently skipped and not saved to your results.

Disable if you want to collect expired listings for market research.

## `skip_ignore_related_jobs` (type: `boolean`):

When enabled, any job whose description contains a word from your 'Keywords to Skip' list is silently skipped and not saved.

Disable if you want those jobs saved but flagged instead. The ignore\_related field will be set to 'True' in the output.

## `scrape_company_details` (type: `boolean`):

When enabled, the scraper visits each company's Indeed profile page to collect extra data: company size, industry, and a short description.

Note: this roughly doubles the number of page loads, so scraper price per result would be approximately doubled.

## `follow_apply_redirect` (type: `boolean`):

When enabled, the scraper resolves the external 'Apply on company site' link to its final destination URL by following redirects. Useful when you want the real company careers page URL instead of Indeed's redirect.

Slightly slower — only enable if you need resolved URLs.

## `concurrency` (type: `integer`):

Number of parallel browser workers per phase (listing + processing). Higher values can increase speed but also increase resource usage.

## Actor input object example

```json
{
  "search_keywords": "software engineer",
  "search_country": "us",
  "date_filter": "any",
  "start_urls": [],
  "ignore_related": [],
  "min_match_percentage": 0,
  "max_jobs": 25,
  "per_company_jobs": 5,
  "ai_matching_enabled": true,
  "save_unique_only": true,
  "skip_expired_jobs": true,
  "skip_ignore_related_jobs": true,
  "scrape_company_details": false,
  "follow_apply_redirect": false,
  "concurrency": 5
}
```

# Actor output Schema

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

Scraped Indeed job listings with title, salary, company, location, AI match score, and apply links.

# 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 = {
    "search_keywords": "software engineer",
    "search_location": "",
    "start_urls": [],
    "processed_job_urls": "",
    "ignore_companies": "",
    "ignore_related": [],
    "google_sheet_url": ""
};

// Run the Actor and wait for it to finish
const run = await client.actor("hamas/indeed-ai-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 = {
    "search_keywords": "software engineer",
    "search_location": "",
    "start_urls": [],
    "processed_job_urls": "",
    "ignore_companies": "",
    "ignore_related": [],
    "google_sheet_url": "",
}

# Run the Actor and wait for it to finish
run = client.actor("hamas/indeed-ai-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 '{
  "search_keywords": "software engineer",
  "search_location": "",
  "start_urls": [],
  "processed_job_urls": "",
  "ignore_companies": "",
  "ignore_related": [],
  "google_sheet_url": ""
}' |
apify call hamas/indeed-ai-scraper --silent --output-dataset

```

## MCP server setup

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