Indeed Jobs Scraper — Salary, Rating, Full Description
Pricing
from $2.00 / 1,000 job postings
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
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
4 days ago
Last modified
Categories
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") returnssalaryMax: null, never Indeed's internal-1. - ⭐ Employer rating + review count on the same row. No rating means
null, not a misleading0. - 📝 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 —
postedAtandcreatedAtas ISO timestamps, 100% filled in measurement. - 🏷️ Job type and schedule —
employmentType83% 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
- 💵
minSalaryfilter that converts hourly, weekly and monthly pay to a yearly figure - 🏠
remotefilter that never guesses: unknown stays unknown (isRemote: null) - ⏱️
postedWithinDays(1, 3, 7, 14 — the windows Indeed itself accepts) - 📄
withDescriptiontoggle: 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)
- Say what you are looking for — one or more
queries(job titles or keywords). - Say where —
locations(cities or states as Indeed spells them) and, if not the US, acountry. - Run it. Rows arrive in the dataset as they are found; set
withDescription: falsefor 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}
| field | type | default | what it does |
|---|---|---|---|
queries | array | — | Job titles / keywords, one search line each |
locations | array | — | Cities, states or regions; empty = whole country |
country | enum | us | Which national Indeed site (23 supported) |
maxItems | integer | 100 | Hard cap on delivered postings (max 5,000) |
withDescription | boolean | true | Add the full description to every row (one bulk request per slice) |
postedWithinDays | enum | — | 1, 3, 7 or 14 |
remote | enum | any | any, only, exclude |
minSalary | integer | — | Keep postings whose published yearly salary reaches this |
proxyConfiguration | object | Unblocker | Indeed 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
| field | type | notes |
|---|---|---|
jobId | string | Indeed's job key (16 hex chars) |
title · companyName | string | 100% filled in measurement |
companyRating · companyReviewCount | number | null | null when the employer has no rating |
companyUrl | string | null | Indeed company profile |
location · city · state · country | string | As published |
isRemote | boolean | null | null when Indeed does not say |
salaryText | string | null | Indeed's own wording, e.g. From $50 an hour |
salaryMin · salaryMax | number | null | One-sided range → the missing end is null |
salaryPeriod | string | null | hour, day, week, month, year |
salaryCurrency | string | null | ISO code |
employmentType | string | null | Full-time, Part-time, Contract… 83% filled in measurement |
schedule | string | null | Monday to Friday, Day shift, 8 hour shift |
benefits | array | null | 401(k), Dental insurance, Paid time off… null when none is published |
tags | array | Every Indeed attribute on the card |
snippet | string | Short teaser from the result list |
description | string | null | Full text, plain (only with withDescription) |
postedAt · createdAt | ISO date | null | |
sponsored · easyApply | boolean | |
url | string | Canonical https://<site>/viewjob?jk=… |
searchQuery · searchLocation · scrapedAt | string | Which 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 ApifyClientclient = 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:
| event | price | when |
|---|---|---|
apify-actor-start | $0.00005 | once per run (per GB of run memory; the default 512 MB run counts as one) |
keyword-search | $0.005 | one keyword × location search that delivered at least one posting |
job | $0.002 | one delivered posting |
job-description | $0.004 | only 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
proxyConfigurationuses 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 wideningpostedWithinDays. - 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.
⚖️ Is it legal to scrape this data?
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
- Issues and feature requests: the Issues tab of this Actor
- Website: foxlabs.com.tr
- More Actors: apify.com/foxlabs
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-searchevent ($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) andschedule.employmentTypenow reads Indeed's taxonomy (83% filled instead of 6%). validThroughremoved: its only source is the login-walled posting page.salaryCurrencyis no longer filled when no salary figure is published.detailDelayMsremoved — 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.