Indeed Jobs Scraper — Salary, Rating, Full Description avatar

Indeed Jobs Scraper — Salary, Rating, Full Description

Pricing

from $2.00 / 1,000 job postings

Go to Apify Store
Indeed Jobs Scraper — Salary, Rating, Full Description

Indeed Jobs Scraper — Salary, Rating, Full Description

Indeed job postings with the published salary (structured min/max), the employer's star rating and the full job description. Search by keyword, location, country, posting date, remote and minimum salary. One row per job, closed and placeholder listings are dropped and never charged.

Pricing

from $2.00 / 1,000 job postings

Rating

0.0

(0)

Developer

Berkan Kaplan

Berkan Kaplan

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

4 days ago

Last modified

Share

Indeed Jobs Scraper — Salary, Rating & Full Description 🚀

foXLabs hiring series: Job Postings Database · Greenhouse · Lever · Ashby · Workday · SmartRecruiters · Personio · Teamtailor · Workable · BambooHR · Google Jobs

🎉 Indeed job postings as clean rows: the published salary as structured numbers (min, max, period), the employer's star rating and review count, the full job description (measured median 3,695 characters), location, posting date, employment type and a direct link. Search by keyword, location, country, posting age, remote and minimum salary. Built for sales & competitive-intelligence teams scoring hiring intent, recruiters, job boards and salary-data pipelines.

🔍 What is the Indeed Jobs Scraper — and when should you use it?

Indeed is the largest job aggregator in most countries, and it publishes two things that company career sites usually do not: a salary estimate or range and an employer rating. This Actor reads Indeed's own search payload (not fragile HTML selectors), then opens each posting to add the full description and the employer's structured salary from the posting's own metadata.

Use it when you need: salary benchmarks by role and city · employers hiring for a function right now, with their rating · a job feed with full descriptions for an LLM pipeline · a daily list of new postings in a market.

Use something else when: you want every open role of specific companies straight from their own careers site — then use the foXLabs hiring series above, or Job Postings Database, which keeps a daily-refreshed index of 440,000+ roles from ~33,000 Greenhouse, Lever, Ashby, SmartRecruiters, BambooHR, Personio and Teamtailor boards and answers one query across all of them.

How coverage works — read this before you set maxItems. Indeed puts deep pagination behind a login: page 2 of any search redirects to its sign-in screen (measured 2026-10-20, with a real browser). This Actor therefore does not page — each keyword × location pair returns up to ~45 postings, and coverage grows by adding more pairs. Want 1,000 nursing jobs in Texas? Give 25 city lines instead of one state line. No Actor can give you Indeed's full result set without an account, and this one will not pretend otherwise.

🤖 Use with AI agents

Already on the Apify MCP server? Ask for this Actor by name: foxlabs/indeed-jobs-scraper.

Your agent can pay for its own runs. This Actor is pay-per-event with agentic payments, so an agent can discover it, run it and settle the bill over x402 (USDC on Base) or Skyfire — no Apify account or API token of its own. Billing is $0.005 per search that delivered rows plus $0.002 per delivered posting ($0.004 more when its full description comes back); errors, blocked pages and searches that find nothing are free.

Otherwise paste this into Claude, ChatGPT, Cursor or any MCP-enabled assistant:

I want Indeed job postings with salary and employer rating using the Apify Actor `foxlabs/indeed-jobs-scraper`.
Input: `queries` is a list of job titles/keywords, `locations` is a list of cities or states.
Each keyword × location pair returns up to ~45 postings (Indeed hides deeper pages behind a login),
so to widen coverage add more pairs rather than raising `maxItems`.
Optional: `country` (us, uk, de, ca, au…), `postedWithinDays` (1|3|7|14), `remote` (any|only|exclude),
`minSalary` (yearly), `withDescription` (true adds the full description; it costs one extra bulk request per search, not one page per posting).
Start with: {"queries":["registered nurse"],"locations":["Texas"],"maxItems":50,"withDescription":true}
Ask me which roles and cities I care about, run the Actor, then summarise salary ranges per city as a table.

The machine-readable API, MCP config and OpenAPI definition live at apify.com/foxlabs/indeed-jobs-scraper.md.

📋 Overview

Why teams pick this one:

  • 💰 Salary as numbers, not a string — salaryMin, salaryMax, salaryPeriod, salaryCurrency. A one-sided range ("From $50 an hour") returns salaryMax: null, never Indeed's internal -1.
  • ⭐ Employer rating + review count on the same row. No rating means null, not a misleading 0.
  • 📝 Full description on every row — measured 47 of 47 postings, median 3,847 characters. All descriptions of one keyword × location slice arrive in a single extra request, so turning them on costs seconds, not minutes.
  • 🎁 Benefits as a list — 401(k), Dental insurance, Tuition reimbursement… published on 67% of postings in measurement. Most Indeed scrapers never surface this.
  • 🗓️ Real dates — postedAt and createdAt as ISO timestamps, 100% filled in measurement.
  • 🏷️ Job type and schedule — employmentType 83% filled, schedule ("Monday to Friday", "Day shift") where Indeed publishes it, plus Indeed's own tag list.
  • 🚫 No decoy rows. Indeed's result DOM carries placeholder job keys that lead nowhere; rows are built only from the page's embedded job data, so a decoy is never delivered or charged.
  • 🌍 23 country sites — us, uk, ca, au, de, fr, nl, es, it, ie, in, sg, nz, za, pl, se, ch, at, be, br, mx, tr, ae.

✨ Features

  • 🔎 Keyword × location slicing with automatic de-duplication across slices
  • 💵 minSalary filter that converts hourly, weekly and monthly pay to a yearly figure
  • 🏠 remote filter that never guesses: unknown stays unknown (isRemote: null)
  • ⏱️ postedWithinDays (1, 3, 7, 14 — the windows Indeed itself accepts)
  • 📄 withDescription toggle: one bulk request per slice, so the full text costs almost no extra time
  • 🧾 One flat row shape, same keys in the same order, every time
  • 🤖 MCP + x402 ready, with input and output schemas published

🎬 Quick Start

curl -X POST "https://api.apify.com/v2/acts/foxlabs~indeed-jobs-scraper/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"queries": ["registered nurse"], "locations": ["Texas"], "maxItems": 20, "withDescription": true}'

🚀 Getting Started (3 steps)

  1. Say what you are looking for — one or more queries (job titles or keywords).
  2. Say where — locations (cities or states as Indeed spells them) and, if not the US, a country.
  3. Run it. Rows arrive in the dataset as they are found; set withDescription: false for a quick, cheap scan first.

📥 Input

{
"queries": ["registered nurse", "nurse practitioner"],
"locations": ["Houston, TX", "Austin, TX", "Dallas, TX"],
"country": "us",
"maxItems": 200,
"withDescription": true,
"postedWithinDays": "7",
"remote": "any",
"minSalary": 70000
}
fieldtypedefaultwhat it does
queriesarray—Job titles / keywords, one search line each
locationsarray—Cities, states or regions; empty = whole country
countryenumusWhich national Indeed site (23 supported)
maxItemsinteger100Hard cap on delivered postings (max 5,000)
withDescriptionbooleantrueAdd the full description to every row (one bulk request per slice)
postedWithinDaysenum—1, 3, 7 or 14
remoteenumanyany, only, exclude
minSalaryinteger—Keep postings whose published yearly salary reaches this
proxyConfigurationobjectUnblockerIndeed refuses plain and datacenter requests and challenges residential IPs; Apify's Unblocker is the default and should stay

At least one queries or locations entry is required. A bad value is a stated error, never a silently ignored filter.

📤 Output

fieldtypenotes
jobIdstringIndeed's job key (16 hex chars)
title · companyNamestring100% filled in measurement
companyRating · companyReviewCountnumber | nullnull when the employer has no rating
companyUrlstring | nullIndeed company profile
location · city · state · countrystringAs published
isRemoteboolean | nullnull when Indeed does not say
salaryTextstring | nullIndeed's own wording, e.g. From $50 an hour
salaryMin · salaryMaxnumber | nullOne-sided range → the missing end is null
salaryPeriodstring | nullhour, day, week, month, year
salaryCurrencystring | nullISO code
employmentTypestring | nullFull-time, Part-time, Contract… 83% filled in measurement
schedulestring | nullMonday to Friday, Day shift, 8 hour shift
benefitsarray | null401(k), Dental insurance, Paid time off… null when none is published
tagsarrayEvery Indeed attribute on the card
snippetstringShort teaser from the result list
descriptionstring | nullFull text, plain (only with withDescription)
postedAt · createdAtISO date | null
sponsored · easyApplyboolean
urlstringCanonical https://<site>/viewjob?jk=…
searchQuery · searchLocation · scrapedAtstringWhich slice produced the row
{
"jobId": "4c58818367959016",
"title": "Private Duty Nurse (RN) – Concierge In-Home Care",
"companyName": "Home Care Assistance",
"companyRating": 3.8,
"companyReviewCount": 251,
"location": "San Antonio, TX 78215",
"isRemote": false,
"salaryText": "From $50 an hour",
"salaryMin": 50,
"salaryMax": null,
"salaryPeriod": "hour",
"salaryCurrency": "USD",
"employmentType": "Contract",
"schedule": "8 hour shift",
"benefits": ["401(k)", "Dental insurance", "Paid time off"],
"tags": ["Contract", "8 hour shift", "Flexible schedule"],
"postedAt": "2026-10-14T05:00:00.000Z",
"easyApply": false,
"url": "https://www.indeed.com/viewjob?jk=4c58818367959016"
}

💼 Use cases

Salary benchmarking by city Input: {"queries":["registered nurse"],"locations":["Houston, TX","Austin, TX","Dallas, TX"],"withDescription":false} Output: rows with salaryMin/salaryMax/salaryPeriod per city. Use: build a pay-band table, or price your own job ad against the market.

Hiring-intent lead list Input: {"queries":["sales manager"],"locations":["Germany"],"country":"de","postedWithinDays":"7"} Output: employers that posted in the last week, with rating and review count. Use: hand sales a list of companies that are visibly growing a function.

Description corpus for an LLM Input: {"queries":["data engineer"],"locations":["Remote"],"withDescription":true,"maxItems":500} Output: full descriptions averaging ~4,000 characters. Use: extract required skills, tools and seniority signals at scale.

🔗 Integration

JavaScript

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });
const run = await client.actor('foxlabs/indeed-jobs-scraper').call({
queries: ['registered nurse'],
locations: ['Texas'],
maxItems: 50,
withDescription: true,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.length, items[0]?.salaryText);

Python

from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("foxlabs/indeed-jobs-scraper").call(run_input={
"queries": ["registered nurse"],
"locations": ["Texas"],
"maxItems": 50,
})
# apify-client 3.x returns a Run object; with client 1.x/2.x use run["defaultDatasetId"]
for item in client.dataset(run.default_dataset_id).iterate_items():
print(item["title"], item["salaryText"])

n8n / Zapier / Make: use the Apify integration, pick this Actor, and map title, companyName, salaryMin, salaryMax, url into your sheet, CRM or Slack message.

📊 Pricing

Pay per event, no monthly rental:

eventpricewhen
apify-actor-start$0.00005once per run (per GB of run memory; the default 512 MB run counts as one)
keyword-search$0.005one keyword × location search that delivered at least one posting
job$0.002one delivered posting
job-description$0.004only when a full description was actually returned

A list-only run (withDescription: false) costs $0.002 per posting. With descriptions, a posting costs $0.006 — and if Indeed does not return a description for a row, the description event is not charged.

The search fee exists because the cost of a search is fixed per keyword × location pair: one result page plus one bulk request that brings every description of that page. A full search of ~45 postings therefore bills $0.005 on top of $0.27 of postings — 1.8% of the invoice. You only ever pay it for a search that actually delivered rows, and a 5-keyword × 3-city run pays it at most 15 times.

Never billed: blocked pages, network errors, searches that return nothing, rows without a description (the description event), postings filtered out by your own settings, and runs that deliver nothing. A run that cannot reach Indeed at all fails instead of reporting "no results".

❓ FAQ

Why do I only get ~45 postings per keyword and location?

Because Indeed hides deeper pages behind a login: page 2 of a search redirects to its sign-in screen. Measured on 2026-10-20 with a real browser, both on the desktop and the mobile site. Coverage grows by slicing — more keywords, more cities, narrower postedWithinDays windows — not by paging.

Do I need an Indeed account?

No. This Actor never logs in and never asks you for credentials.

How often is a salary published?

Indeed shows a salary on part of its listings. In our measurements the share ranged between 40% and 83% depending on the query, so we do not promise a single number. With minSalary set, postings without a published salary are skipped — that is deliberate, so a filter never passes rows it could not check.

Why is salaryMax sometimes null when salaryText says a number?

"From $50 an hour" and "Up to $120,000 a year" are one-sided. Indeed marks the missing end with -1 internally; we return null instead of a nonsense value.

Why is companyRating null for some employers?

Indeed sends 0 when an employer has no rating yet. A zero would read as a one-star employer, so we return null.

How are the descriptions fetched — isn't that slow?

No. The Actor does not open one page per posting. It asks Indeed's own bulk description endpoint for every job key of a slice at once: measured 47 keys in a single request, 1.4 seconds, 47 of 47 descriptions returned. That is also why a description never needs an Indeed login.

What is the keyword-search fee for?

Each keyword × location search costs us the same whether it yields 1 posting or 45: one result page plus one bulk description request. The $0.005 fee covers that fixed part, so the per-posting price can stay at $0.002 instead of carrying it. On a full search it is 1.8% of the bill, and a search that is blocked or finds nothing is never charged.

Why is validThrough not in the output?

It only exists on the posting page, and that page is behind Indeed's login wall (measured HTTP 401 on 2026-10-20, on every route we tried). We would rather drop a field than ship a column that is empty.

Can it run as a real-time API?

The default build is the batch/x402 version. A Standby ("… API") sibling can be published from the same code if there is demand — tell us.

Which countries are supported?

us, uk, ca, au, de, fr, nl, es, it, ie, in, sg, nz, za, pl, se, ch, at, be, br, mx, tr, ae.

🐛 Troubleshooting

  • Run failed with "could not be loaded" or "blocked" — Indeed refuses plain and datacenter requests (HTTP 403 "Security Check") and challenges residential IPs with Cloudflare. The default proxyConfiguration uses Apify's Unblocker, which gets through; if you replaced it, put it back. Nothing is charged in that case.
  • "No job postings matched" — the message names the filters that emptied the result. Try removing minSalary (it drops postings without a published salary) or widening postedWithinDays.
  • Descriptions missing — rerun the slice; the bulk endpoint occasionally answers for only part of a batch, and rows without a description are not charged for one.
  • Fewer rows than maxItems — see the FAQ: coverage comes from slices, not from paging.

⚠️ Trademark

Indeed is a trademark of Indeed, Inc. This Actor is an independent tool and is not affiliated with, endorsed by or sponsored by Indeed.

This Actor reads only publicly visible job postings — no login, no personal data about candidates, no private employer data. Public-data scraping is broadly lawful in the EU and the US, but a website's terms of service are a separate matter from the law: Indeed's terms restrict automated collection, so review your own use case, keep volumes reasonable, and do not republish Indeed's content as your own. You are responsible for how you use the output, including GDPR/CCPA duties if you enrich it with personal data.

🤝 Support & contact

Changelog

See CHANGELOG.md in the Actor's source for the full history.

0.1.10 — 2026-10-20

  • README fix: the AI-agent prompt still described the pre-0.1.5 architecture ("one extra page per posting") and the agent billing line left out the search and description events. No code, input, output or price change.

0.1.9 — 2026-10-20

  • New keyword-search event ($0.005), charged once per keyword × location search that delivered at least one posting — 1.8% of a full 45-posting search. Blocked, failed and empty searches stay free.

0.1.6 — 2026-10-20

  • Default run memory 512 MB (measured peak 58 MB): a 15-posting run finishes in 6 seconds.
  • Pricing published: job $0.002, job-description $0.004, apify-actor-start $0.00005.

0.1.5 — 2026-10-20

  • Descriptions now come from Indeed's own bulk endpoint — 47 of 47 in a single request — instead of one page per posting, which also removes the login wall those pages hit.
  • New columns: benefits (67% filled) and schedule. employmentType now reads Indeed's taxonomy (83% filled instead of 6%).
  • validThrough removed: its only source is the login-walled posting page.
  • salaryCurrency is no longer filled when no salary figure is published.
  • detailDelayMs removed — there are no posting pages left to pace.

0.1 — 2026-10-20

  • First release. Keyword × location slicing, structured salary, employer rating, full description from the posting's structured metadata, 23 country sites, pay-per-event billing with the description event charged only when a description is returned.