Indeed Job Scraper
Pricing
$19.99/month + usage
Indeed Job Scraper
Indeed Job Scraper extracts job listings from Indeed search results. It collects job titles, company names, locations, salaries, job descriptions, posting dates, and job URLs. Ideal for job market research, recruitment insights, salary analysis, and hiring trend monitoring.
Pricing
$19.99/month + usage
Rating
0.0
(0)
Developer
Scraper Engine
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
13 days ago
Last modified
Categories
Share
Indeed Jobs Scraper — Titles, Salaries and Company Data
Indeed Jobs Scraper extracts job listings from Indeed — title, company, location, salary, job type, full description, benefits, and apply links — as structured JSON, with no HTML parsing or manual field mapping required. Search by keyword and location or feed it Indeed search URLs directly, and results are pushed to the dataset row by row as the run progresses. Start a run below and get your first dataset in minutes.
What is Indeed Jobs Scraper?
Indeed Jobs Scraper is an Apify Actor that pulls job listings from Indeed by calling the same GraphQL endpoint Indeed's own mobile app uses (apis.indeed.com/graphql) — no browser, no Playwright, no HTML selectors to maintain. It returns each job as a normalized JSON record with company, location, salary, and apply-link details already parsed out. No Indeed account or login is required to run it. It's built for recruiters, job-market researchers, and developers feeding job data into ATS pipelines, dashboards, or AI agents.
What Indeed job data is publicly available to scrape?
Indeed publishes job postings, company summaries, and salary estimates openly — none of it sits behind a login for a visitor browsing the site.
| Data Category | Publicly Available (no login) | Restricted (requires Indeed account) |
|---|---|---|
| Job title, description, employment type | Yes | — |
| Company name, industry, size, overview | Yes | — |
| Salary estimate / posted range | Yes, when Indeed publishes one | — |
| Job location and posting date | Yes | — |
| Job attributes and benefits tags | Yes | — |
| Applying to a job (Indeed Apply) | — | Requires login |
| Saved jobs, job alerts, application tracking | — | Requires login |
| Direct recruiter messaging | — | Requires login |
Indeed Jobs Scraper only returns publicly visible data — what any visitor sees without signing in. Nothing behind a login wall.
What data can I extract with Indeed Jobs Scraper?
Each run returns job postings with posting details, company and location data, and salary and apply metadata in one normalized record.
Job & posting details
| Field Name | Description |
|---|---|
jobId / job.id / job.jobKey | Indeed's internal job key |
title / job.title / job.displayTitle | Job title as posted |
job.descriptionText / job.jobDescription | Plain-text job description |
job.descriptionHtml / job.jobDescriptionHTML | Raw HTML job description |
job.jobType | Employment type(s) detected (e.g. ["Full-time"]) |
job.isRemote / job.remoteLocation | Whether the posting is flagged remote |
job.attributes | Attribute labels Indeed shows on the card (e.g. "401(k)", "Health insurance") |
job.benefits | Benefit labels extracted from attributes |
job.dates.datePublished / postedText | Publish date and human-readable "posted X days ago" text |
job.hiring.urgentlyHiring / job.hiring.highVolumeHiring | Hiring-urgency flags Indeed sets on the listing |
Company & location details
| Field Name | Description |
|---|---|
companyName / company.name | Employer name |
company.industry / company.numEmployees / company.revenue | Company size and industry, when Indeed supplies it |
company.description / company.briefDescription | Company overview text |
company.branding.logoUrl | Company logo image URL |
company.links.corporateWebsite | Employer's website, when listed |
locationShort / location.formattedShort / location.formattedLong | Location as displayed on the listing |
location.city / location.state / location.stateName / location.postalCode | Parsed location components |
location.country / location.countryCode | Country name and code |
Salary, apply & source metadata
| Field Name | Description |
|---|---|
salaryText / job.salary.text | Salary as displayed (e.g. "$60,000 - $75,000 a year") |
job.salary.min / job.salary.max | Parsed numeric salary bounds |
job.salary.type | Pay period detected: hourly, weekly, monthly, or yearly |
job.salary.currency | Currency code |
jobUrl / apply.jobUrl | Direct link to the job on Indeed |
apply.applyUrl / apply.thirdPartyApplyUrl | Apply link, third-party ATS if applicable |
apply.indeedApplyEnabled | Whether Indeed Apply (one-click) is available |
sourceMeta.source | Always "Indeed" |
🤖 Add-on: Need additional job market data?
Pair this with ../Linkedin-Ads-Scraper to see how the same employers advertise open roles, or ../Google-Search-Results-Scraper to cross-reference company names against broader web mentions. Both are Scraper Engine Actors you can chain into the same pipeline.
Why not build this yourself?
Indeed doesn't offer an accessible public API for general job-search access, so building this yourself means reverse-engineering Indeed's endpoints, maintaining reverse-engineered API headers and credentials that can be rotated or revoked without notice, and building your own proxy-rotation and retry logic to survive rate limiting. Indeed Jobs Scraper already does this: it calls Indeed's mobile-app GraphQL endpoint with the request headers that endpoint expects, and automatically escalates through a proxy fallback ladder (direct → datacenter → residential, 3 retries per tier) when Indeed responds with a 401, 403, or 429. You get normalized JSON output instead of chasing endpoint changes and blocked requests yourself.
How to use Indeed Jobs Scraper
Indeed Jobs Scraper runs as an Apify Actor — no server or scraping infrastructure to host yourself.
- Open Indeed Jobs Scraper on the Apify platform (Apify Console → Actors, or the Actor's Store listing).
- Provide a search Query (e.g. "Sales") and, optionally, a Location — or switch to Start URLs and paste one or more Indeed search/company URLs instead.
- Set optional filters: Country, Radius, Remote/Hybrid, Job level, From days, Sort, and Job type.
- Click Start and watch the run log — jobs are saved to the dataset as they're found, not only at the end.
- Open the Output tab and export results as JSON, CSV, Excel, or the other formats Apify's dataset export supports.
How to scale to bulk job extraction
Use the Start URLs input (urls) to run multiple Indeed searches in one job: each URL is parsed independently, and its own query/location/radius/job-type/sort parameters override the top-level settings. Set Max rows per URL (maxRowsPerUrl) to cap how many jobs each URL contributes when running more than one URL — with a single URL, maxRows alone governs the total. enableUniqueJobs deduplicates by Indeed job key across every URL in the run.
What can you do with Indeed job data?
- 📊 A recruiting-market analyst tracking demand uses
title,location.city, andjob.dates.datePublishedto chart how fast a given role is being posted across regions. - 💰 A compensation researcher benchmarking pay uses
job.salary.min,job.salary.max, andjob.salary.typeto build salary bands for a role by location. - 🏢 A sales or BD rep prospecting growing employers uses
companyName,job.hiring.highVolumeHiring, andcompany.industryto spot companies that are hiring aggressively. - 🔗 A job board or aggregator uses
jobUrlandapply.applyUrlto build outbound listings without re-hosting Indeed's own content. - 🤖 An AI agent developer feeds
job.descriptionText,title, andjob.attributesinto a RAG pipeline or resume-matching agent as structured context instead of scraping and cleaning HTML first.
How does Indeed Jobs Scraper handle rate limits and blocking?
Indeed Jobs Scraper calls Indeed's GraphQL endpoint directly and retries transient failures (5xx responses, network errors) up to 3 times per request with a 1.5-second-per-attempt backoff. If Indeed responds with a 401, 403, or 429 — a sign of a block or rate limit — the Actor escalates to the next proxy tier in its fallback ladder (direct → datacenter → residential when run outside Apify; residential-first when run on Apify), retrying up to 3 times per tier before moving to the next. ⚠️ 4xx client/auth errors are not retried within a tier since they won't resolve with the same credentials — the Actor moves straight to the next proxy tier instead. Progress already saved to the dataset is never lost between tiers or retries.
⬇️ Input
| Parameter | Required | Type | Description | Example Value |
|---|---|---|---|---|
countryCode | No | String | Select the Indeed country domain to scrape from. | "us" |
query | No | String | Job search keywords (e.g. job title, skills). Required when using Option 1. | "Sales" |
location | No | String | Job location for search. Leave empty for no location filter. | "New York, NY" |
maxRows | No | Integer | Maximum number of jobs to scrape in total. | 100 |
radius | No | String | Search radius from location. | "25" |
remote | No | String | Filter by remote or hybrid work. 'Remote' is applied server-side; 'Hybrid' is post-filtered on description/attributes. | "remote" |
level | No | String | Experience level (US only). Post-filtered on title/attributes/description. | "mid_level" |
fromDays | No | String | Only jobs posted in the last N days. NOTE: server-side this is mutually exclusive with jobType / remote — when set, those become post-filters. | "7" |
sort | No | String | Sort results by relevance or date. | "relevance" |
jobType | No | String | Filter by employment type. Full/part-time/contract/internship are server-side; temporary/permanent/seasonal/freelance are post-filtered on text. | "fulltime" |
urls | No | Array | List of Indeed search or company URLs. When provided, each URL becomes its own search; URL parameters override the top-level config (Option 1 is bypassed). | ["https://www.indeed.com/jobs?q=sales&l=New+York%2C+NY"] |
maxRowsPerUrl | No | Integer | Optional per-URL cap when using Option 2 with multiple URLs. Leave empty to let maxRows be the only ceiling. | 50 |
includeSimilarJobs | No | Boolean | No-op for this mobile-API path — Indeed's GraphQL endpoint doesn't return "View similar jobs" placeholder cards. Kept for input-schema compatibility. | true |
enableUniqueJobs | No | Boolean | Deduplicate by Indeed job key across pages and URLs. | true |
proxyConfiguration | No | Object | By default no proxy is used (the mobile API usually doesn't need one). On 401/403/429 the Actor falls back to datacenter then residential proxy (3 retries per tier). When running on Apify, the default tier is residential. | {"useApifyProxy": false} |
No input is strictly required — omit query and urls and the Actor errors out, since it needs one of the two to know what to search for.
Example input
{"countryCode": "us","query": "Sales","location": "New York, NY","maxRows": 100,"radius": "25","remote": "","level": "","fromDays": "7","sort": "relevance","jobType": "fulltime","urls": [],"maxRowsPerUrl": 50,"includeSimilarJobs": true,"enableUniqueJobs": true,"proxyConfiguration": {"useApifyProxy": false}}
⬆️ Output
Every run pushes typed, normalized JSON to the Apify dataset — one record per job, with a consistent schema across runs. Export as JSON, CSV, Excel, XML, RSS, or HTML directly from the dataset's Export tab. The default dataset view surfaces jobId, title, companyName, locationShort, postedText, salaryText, and jobUrl as table columns, but every record carries the full nested structure below.
Example output
{"jobId": "3f9a1c2e7b4d5e6f","title": "Sales Representative","normalizedTitle": null,"companyName": "Acme Retail Group","locationShort": "New York, NY","postedText": "2 days ago","salaryText": "$55,000 - $70,000 a year","jobUrl": "https://www.indeed.com/viewjob?jk=3f9a1c2e7b4d5e6f","job": {"id": "3f9a1c2e7b4d5e6f","jobKey": "3f9a1c2e7b4d5e6f","title": "Sales Representative","displayTitle": "Sales Representative","normalizedTitle": null,"descriptionText": "Acme Retail Group is looking for a motivated Sales Representative to join our growing team...","descriptionHtml": "<p>Acme Retail Group is looking for a motivated Sales Representative...</p>","jobDescription": "Acme Retail Group is looking for a motivated Sales Representative to join our growing team...","jobDescriptionHTML": "<p>Acme Retail Group is looking for a motivated Sales Representative...</p>","language": "en","locale": "en_US","isRemote": false,"remoteLocation": false,"jobType": ["Full-time"],"shiftAndSchedule": ["Monday to Friday"],"workingSystem": [],"occupation": [],"requirements": [],"attributes": ["401(k)", "Health insurance", "Paid time off"],"benefits": ["401(k)", "Health insurance", "Paid time off"],"salary": {"text": "$55,000 - $70,000 a year","currency": "USD","type": "yearly","min": 55000,"max": 70000,"weeklyMin": 1058,"weeklyMax": 1346,"source": "EXTRACTION"},"hiring": {"urgentlyHiring": false,"highVolumeHiring": false,"isRepost": false,"organicApplyStarts": null,"numOfCandidates": null},"dates": {"postedText": "2 days ago","formattedRelativeTime": "2 days ago","postedToday": false,"datePublished": "2026-08-14","pubDateEpoch": 1755129600000},"status": {"expired": false,"newJob": false,"visited": false}},"company": {"name": "Acme Retail Group","displayName": "Acme Retail Group","industry": "Retail","revenue": null,"numEmployees": "201 to 500","foundedYear": null,"description": "Acme Retail Group operates specialty retail stores across the Northeast.","briefDescription": "Acme Retail Group operates specialty retail stores across the Northeast.","rating": { "score": null, "count": null },"ceo": { "name": null, "photoUrl": null, "startDate": null },"branding": {"logoUrl": "https://d2q79iu7y748jz.cloudfront.net/s/_squarelogo/acme.png","headerUrl": null,"brandColor": null,"showJobBranding": false},"links": {"corporateWebsite": "https://www.acmeretail.example","careers": null,"facebook": null,"instagram": null,"twitter": null,"indeedCompanyPage": "https://www.indeed.com/cmp/Acme-Retail-Group"},"addresses": [],"emails": []},"location": {"countryCode": "US","country": "United States","state": "NY","stateName": "New York","city": "New York","postalCode": "10001","streetAddress": null,"formattedShort": "New York, NY","formattedLong": "New York, NY 10001","latitude": null,"longitude": null},"apply": {"jobUrl": "https://www.indeed.com/viewjob?jk=3f9a1c2e7b4d5e6f","applyUrl": "https://www.indeed.com/applystart?jk=3f9a1c2e7b4d5e6f","originalApplyUrl": "https://www.indeed.com/applystart?jk=3f9a1c2e7b4d5e6f","thirdPartyApplyUrl": "https://www.indeed.com/applystart?jk=3f9a1c2e7b4d5e6f","indeedApplyEnabled": true},"sourceMeta": {"source": "Indeed","feedId": null,"fccId": null,"scrapingInfo": { "page": 1, "index": 0 },"rankingScores": { "bid": null, "bidPosition": null, "eApply": null, "eQualified": null }},"_raw": {"id": "3f9a1c2e7b4d5e6f","company": "Acme Retail Group","title": "Sales Representative"}}
_raw carries the complete pre-transform record the Actor built from Indeed's GraphQL response — the same source values that feed every field above, plus a few internal-only helpers (companyBrandingAttributes, jobLocationCity/jobLocationState, _mobileExtras) kept for debugging. ⚠️ Some fields in the schema — company.rating, company.foundedYear, location.latitude/longitude, company.links.facebook/instagram/twitter, and job.occupation/job.requirements — are always null or empty because Indeed's mobile GraphQL endpoint doesn't return that data at all, not because of a scraping failure. They're kept in the schema for structural consistency across records.
How does it work?
Indeed Jobs Scraper sends authenticated GraphQL requests directly to Indeed's mobile-app API endpoint — the same one the Indeed iOS app calls — instead of rendering pages in a browser or parsing HTML. Each request page returns up to 100 job cards at a time, which the Actor walks via cursor-based pagination until it hits your maxRows target or runs out of results. Requests go out either directly or through Apify's proxy network, escalating tiers automatically if Indeed responds with a block. Only data Indeed serves through this public-facing endpoint is returned — nothing requiring a login. Because the Actor targets a stable API contract rather than page markup, the output schema stays the same even when Indeed changes its website's visual layout.
Integrations
Indeed Jobs Scraper runs on Apify, so it works with anything that can call the Apify API or use Apify's official client libraries and no-code connectors.
Calling Indeed Jobs Scraper programmatically
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_API_TOKEN>")run = client.actor("scraper-engine/indeed-jobs-scraper").call(run_input={"query": "Sales","location": "New York, NY","maxRows": 100,})for item in client.dataset(run["defaultDatasetId"]).iterate_items():print(item["title"], item["companyName"], item["salaryText"])
Works in Go, Ruby, Node.js, cURL — any language that can make an HTTP request to Apify's REST API.
No-code tools (n8n, Make, LangChain)
In n8n, use the Apify node (or an HTTP Request node pointed at the Actor's run endpoint) to trigger a run and pull dataset items into your workflow. In Make, the Apify app module lets you start the Actor and route each output job into downstream scenarios like a CRM or spreadsheet. In LangChain, use the ApifyWrapper / Apify dataset loader to pull results straight into a document loader for RAG pipelines.
Is it legal to scrape Indeed job listings?
Scraping publicly available job listing data is generally legal — Indeed Jobs Scraper only accesses data any visitor can see on Indeed without logging in. Job postings, company names, and salary ranges are business and product data, not personal data, so this falls under Indeed's Terms of Service and database-rights considerations rather than GDPR or CCPA. One caveat: the company.ceo field occasionally carries a named individual's name and photo — if you store or process that, treat it with the same care you'd apply to any personal data, since it identifies a natural person even in a business context. Consult legal counsel if your use case involves bulk storage of personal data or if you plan to redistribute Indeed's content commercially.
Frequently asked questions
What Indeed job fields does Indeed Jobs Scraper return?
The core fields are title, companyName, locationShort, salaryText, and jobUrl, plus the full nested job, company, location, and apply objects with dozens of sub-fields. See What data can I extract above for the complete breakdown.
Does Indeed Jobs Scraper require an Indeed account or login?
No. The Actor calls Indeed's public mobile-app GraphQL endpoint directly and doesn't authenticate as an Indeed user — no login, cookies, or Indeed account credentials are needed to run it.
How many jobs can I extract in one run?
maxRows accepts any value from 1 up to 10,000 jobs per run. When using multiple Start URLs, maxRowsPerUrl (1 to 1,000) can additionally cap how much any single URL contributes toward that total.
What happens if a search returns zero results?
The run completes normally with zero dataset items rather than throwing an error — the Actor logs "no more results" and exits the pagination loop as soon as Indeed returns an empty results page for that search.
Can I scrape multiple Indeed searches or URLs at once?
Yes. Provide multiple entries in the urls array (Option 2) and each one runs as an independent search with its own query/location/filters, all deduplicated together when enableUniqueJobs is on.
What's the difference between the Query option and the Start URLs option?
The Query option (query + location + filters) builds one search from your top-level inputs. The Start URLs option (urls) parses one or more Indeed search or company URLs directly — each URL's own q, l, radius, fromage, jt, and sort parameters override the top-level settings for that search, and providing any URLs bypasses the Query option entirely.
Does Indeed Jobs Scraper work with Claude, ChatGPT, and other AI agent tools?
Yes — since it runs as a standard Apify Actor, it's callable as an HTTP endpoint through the Apify API by any agent framework or MCP-compatible client that can trigger Apify Actors, including Apify's own Actors MCP server.
Does Indeed Jobs Scraper return data in a format LLMs can use directly?
Yes. Output is typed, normalized JSON with consistent field names across every run — no HTML parsing or CSS selectors required. Pass it straight to an LLM prompt, index it into a vector store, or feed it to an agent tool.
What happens when Indeed changes its layout or anti-bot system?
Indeed Jobs Scraper targets Indeed's GraphQL API contract rather than page markup, and the output schema is designed to stay stable across Indeed UI changes. The Actor is actively maintained; no specific update turnaround time is guaranteed.
Can I use Indeed Jobs Scraper without managing proxies or browser infrastructure?
Yes. There's no browser to run — the Actor makes direct API requests — and proxy handling (including the fallback ladder to datacenter and residential tiers on a block) is automatic. You only need to configure proxyConfiguration if you want to override the default behavior.
Which Indeed job fields work best for AI training data and RAG indexing?
For RAG, index job.descriptionText, title, and job.attributes — the highest-information text fields. For structured training data, job.salary.min, job.salary.max, job.dates.datePublished, and job.jobType return as consistent typed primitives across every record.
Related scrapers
| Scraper Name | What it extracts |
|---|---|
| ../Linkedin-Ads-Scraper | Employer ad campaigns on LinkedIn — useful for cross-referencing companies also hiring on Indeed |
| ../Google-Search-Results-Scraper | SERP results — for broader company and market research alongside job data |
| ../Shopify-Merchant-Scraper | Merchant/store details — for researching employers that run e-commerce operations |
| ../Amazon-De-Sellers-Leads-Scraper | Seller contact and business data — a comparable lead-generation dataset for B2B research |
Your feedback
Found a bug or missing a field? Let us know at dev.scraperengine@gmail.com — reports like these directly shape what gets fixed and added next.