Indeed Jobs Scraper: Salaries, Full Descriptions, No Login
Pricing
from $2.00 / 1,000 job with full descriptions
Indeed Jobs Scraper: Salaries, Full Descriptions, No Login
Collect public Indeed jobs with salary ranges, company, location and optional full descriptions. Filter titles and schedule a monitor that skips jobs already delivered. No login required.
Pricing
from $2.00 / 1,000 job with full descriptions
Rating
0.0
(0)
Developer
Agnes Maina
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
5 days ago
Last modified
Categories
Share
Indeed Jobs Scraper: salaries, full descriptions and a new jobs monitor
This Indeed jobs scraper collects public job postings with company, location, salary ranges and posted dates. Full descriptions are enabled by default and collected when available. You can filter titles and schedule a monitor that returns only jobs you have not received before. No Indeed login or account cookies are needed.
The actor has two output modes: jobs returns one row per job, and companies returns one row per hiring company.
In five Apify platform runs on October 7, 2026, charged job events matched delivered rows every time. Descriptions succeeded for 63 of 63 jobs: 48 in Chicago and 15 in London. An onlyNew repeat run skipped 28 seen jobs and delivered only new jobs, with zero overlap. These are measured results, not a promise of complete coverage.
Indeed sends logged out visitors to a login page after the first search page. This actor reads page 1 of several narrower versions of your search instead. A measured Chicago registered nurse search grew from 47 unique jobs on the base page to 225 across nine variants. That measurement included a wider radius; this actor keeps your radius or narrows it, so its coverage can differ. It never promises a fixed number of jobs.
What you get
In jobs mode, every delivered row is one job. Missing source values are null.
| Field | What it holds |
|---|---|
jobId | Indeed job key |
url | Canonical job URL on the source domain |
title | Job title |
companyName | Hiring company |
companyRating, companyReviewCount | Published rating and review count; an unavailable zero rating is null |
location, city, state, country | Source location fields; country is not inferred from the search |
salaryText | Published salary text |
salaryMin, salaryMax | Published numeric salary bounds |
salaryCurrency, salaryPeriod | Published currency and period, such as USD and yearly or hourly |
jobTypes, benefits | Published labels, when available |
snippet | Plain text search card excerpt |
descriptionText, descriptionHtml | Full description when requested and available |
postedAt | ISO timestamp from the source publication timestamp |
postedAtRaw | Date text shown by Indeed |
isSponsored | Source sponsored flag |
searchKeywords, searchLocation, searchUrl | Search that first supplied the job |
scrapedAt | Collection timestamp |
In jobs mode, applicant counts, recruiter contacts, email addresses and anything behind login are not returned. Salary values are not estimated or converted. Descriptions are collected from public Indeed responses without logging in.
Each run writes RUN_SUMMARY and DIAGNOSTICS outside the job dataset. They contain variants tried, variants with cards, unique source jobs per search, delivered rows, description outcomes, request counts and stop reasons.
Try it in 30 seconds
- Open Indeed Jobs Scraper in Apify Console.
- Enter keywords, location and country, or paste your Indeed search URLs.
- Set
maxJobsto 20 for a first run. Leave full descriptions enabled or turn them off for cheaper rows. - Click Start, then download the results as JSON, CSV or Excel.
{"keywords": "registered nurse","location": "Chicago, IL","country": "us","maxJobs": 20}
Example output
Selected fields from a captured Indeed search result with descriptions disabled. A delivered row contains all fields listed above.
{"jobId": "9222a6307b90b210","url": "https://www.indeed.com/viewjob?jk=9222a6307b90b210","title": "Data Engineer","companyName": "Resilience","companyRating": 3.2,"companyReviewCount": 33,"location": "Iowa City, IA","city": "Iowa City","state": "IA","country": "US","salaryText": "$100,000 - $140,000 a year","salaryMin": 100000,"salaryMax": 140000,"salaryCurrency": "USD","salaryPeriod": "yearly","descriptionText": null,"descriptionHtml": null,"isSponsored": false}
Pricing
You pay per delivered row:
| Event | When it applies | Price |
|---|---|---|
job-with-description | Delivered row has full description text | $0.002, or $2 per 1,000 |
job | Delivered row has no full description | $0.001, or $1 per 1,000 |
company | Delivered company row with its Indeed company profile | $0.01, or $10 per 1,000 |
owner-email | Company row with a verified owner email | $0.03, or $30 per 1,000 |
apify-actor-start | Platform start event | Added by Apify at run start; see Console pricing |
In companies mode, 200 company rows with an Indeed company profile cost $2.
In jobs mode, one delivered row gets exactly one job event. For example, 500 rows with descriptions cost $1.00 and 500 without cost $0.50: $1.50 in job events, plus the platform start event. If a description cannot be collected, the row uses the cheaper job event. All five reviewed platform runs charged exactly the events matching their delivered rows.
Rows for companies without an Indeed company page, or whose page fetch fails, are delivered free and marked charged: false.
Duplicates, title filtered rows and jobs skipped by onlyNew are never written or charged. The actor checks the remaining budget before each dataset write and stops when the next row is not affordable. Restart recovery skips rows already delivered in the same run.
Input
| Field | Type | Default | What it does |
|---|---|---|---|
output | string | jobs | jobs or companies. |
maxCompanies | integer | 50 | Companies mode only, from 1 to 2,000. Stop collecting when this many distinct companies are found, counting only companies with new jobs when onlyNew is true. |
searchUrls | array of strings | none | Up to 20 supported HTTPS Indeed /jobs URLs. Use these or keywords and location. |
keywords | string | prefill data engineer | Search phrase, required with location when URLs are absent. |
location | string | prefill Remote | Location sent to Indeed. Matching is controlled by Indeed. |
country | string | us | us, uk, de, au or ca. URLs determine their own domain and proxy country. |
postedWithinDays | integer | any age | 1, 3, 7 or 14, sent as fromage. |
titleIncludes | array of strings | none | Title must contain at least one phrase, ignoring case. |
titleExcludes | array of strings | none | Skip titles containing any phrase, ignoring case. |
includeDescription | boolean | true | Collect the full job description for each job. Rows without one cost less. Forced false in companies mode. |
maxJobs | integer | 100 | Global delivery cap, from 1 to 5,000. Ignored in companies mode. |
maxConcurrency | integer | 5 | Concurrent search sessions, from 1 to 20. Each session processes its variants and descriptions in order. |
onlyNew | boolean | false | Skip jobs delivered for the same search and title filters within 90 days. |
proxyConfiguration | object | Apify RESIDENTIAL | Residential proxy is required. Direct, custom and datacenter proxies are refused. |
{"keywords": "data engineer","location": "Remote","country": "us","postedWithinDays": 7,"titleIncludes": ["engineer"],"titleExcludes": ["intern"],"includeDescription": true,"maxJobs": 100}
Supported URL filters are q, l, radius, fromage, sort, jt, remotejob, sc, salary, filter and limit. Tracking parameters, selected job IDs and starting offsets are removed. Unknown filters are rejected. Only the first page of each variant is requested. Variants use newest ordering, tighter posting ages and smaller radii. Job type variants are added only when no job type is set. Remote and experience variants preserve existing user filters. Variants run in measured yield order, with at most 40 per base search. A search stops after three consecutive variants each add fewer than three unique source jobs. Jobs are deduplicated across all searches; results are not globally sorted by date.
Proxy country follows the domain: US for www.indeed.com, GB for uk.indeed.com, DE for de.indeed.com, AU for au.indeed.com, and CA for ca.indeed.com. Omit the proxy country setting when mixing domains.
Schedule the new jobs monitor
- Save a task with your search and
onlyNew: true. - Add it to an Apify schedule, such as once each morning.
- Route the dataset to your destination after the run succeeds.
The first run returns matching jobs up to the cap. Later runs skip IDs delivered under the same search URLs and title filters within 90 days. Changing maxJobs, concurrency or the description option does not reset the history. Capped and filtered jobs are not marked delivered. History lives in the actor's named store indeed-jobs-monitor; deleting its configuration record resets that monitor. Use one schedule per configuration and do not overlap runs for the same configuration. In companies mode, a company comes back when it has an unseen role listed after its last delivery; a one day margin can also bring back a role listed on the delivery day that the earlier run did not collect.
With onlyNew, history is saved every 50 delivered rows and once at the end, including errors and shutdown. A hard crash can leave at most 50 rows missing from saved history, so at most 50 rows can repeat in a later run. Restart recovery reads delivered job IDs from the current run's dataset.
Hiring companies mode
Set output to companies to group jobs from the same search variants by hiring company. Collection stops at maxCompanies; maxJobs is ignored and job descriptions are disabled. Companies are delivered by openRoles descending, with ties in discovery order.
{"keywords":"registered nurse","location":"Chicago, IL","output":"companies","maxCompanies":20}
Every company row contains the following fields, with missing values set to null:
| Fields | Source and meaning |
|---|---|
companyKey, companyName, indeedCompanyUrl | Company slug from the search card link, the card's company name, and its public Indeed company page on the search domain. Without a page link, the key uses the normalized company name. |
website, domain | Website listed on the company's public Indeed page and its domain. Subdomains are stripped only when every removed label is careers style (such as www, jobs or careers). Job boards, hiring platforms, site builders, link pages and social hosts do not qualify as a company domain. |
charged | True for rows billed with the company event; false for rows without a fetched profile and for unpriced local runs. A failed charge stops the run with an error. |
industry, employeeRange, revenueRange, founded, headquarters, description | Public Indeed company page fields. Description is plain text, capped at 500 characters. |
companyRating, companyReviewCount | First job card that supplied the company. |
ceoName, ceoApproval | CEO as listed on Indeed and published approval percentage. The listing does not establish legal ownership. |
ownerFirstName, ownerLastName | First person in the listed CEO name, with titles and credentials removed. A single name has no last name. |
ownerEmail, ownerEmailStatus, ownerEmailPattern | Owner email lookup result. Status is verified, probable (a pattern guess the mail server could not confirm), unknown, no_domain, no_owner_name, skipped_budget or relay_unavailable, and not_requested when Find owner email is off. Only verified is charged. |
openRoles, newJobCount | Distinct matching jobs found this run and the number qualifying as new under monitor history. These are search results, not the company's total vacancies. |
roleTitles, locations | Unique job titles and locations, each capped at 25. |
newestPostedAt, oldestPostedAt | Newest and oldest publication timestamps among the jobs found this run. |
jobs | Up to 50 jobs, new first. Each has jobId, title, url, location, salaryText, postedAt, and isNew. |
searchKeywords, searchLocation, searchUrl | First search that found the company. |
scrapedAt | Collection timestamp. |
Selected fields from the captured PerfectServe company page, with no email lookup requested:
{"companyKey": "perfectserve","companyName": "PerfectServe","indeedCompanyUrl": "https://www.indeed.com/cmp/Perfectserve","website": "http://www.perfectserve.com","domain": "perfectserve.com","ceoName": "Guillaume Castel","ceoApproval": 59,"ownerFirstName": "Guillaume","ownerLastName": "Castel","employeeRange": "201 to 500","revenueRange": "$25M to $100M","founded": 1997,"headquarters": "Knoxville, TN","industry": "Information Technology","ownerEmail": null,"ownerEmailStatus": "not_requested","ownerEmailPattern": null}
Turn on Find owner email to check the work email of the CEO listed on Indeed. The actor asks the company's mail server whether that mailbox exists and never sends an email. Only verified addresses are charged with owner-email. Catch all domains and inconclusive answers come back probable with a pattern guess, free. Apify blocks the mail port, so the check runs on the developer's own mailbox check server. That server receives the owner's first name, last name and company domain, and logs only the domain and the outcome.
If the company event no longer fits your spending limit, delivery stops with reason budget. The dataset write happens before the charge, and an uncertain write or charge stops the run. Ten company page failures in a row stop the run with reason company_pages_failing.
With onlyNew, a previously delivered company comes back when it has an unseen role listed after its last delivery; a one day margin can also bring back a role listed on the delivery day that the earlier run did not collect. Listing time is the later of the source publication and creation dates. Roles with neither date do not bring that company back. Its row includes both old and new jobs, with newJobCount counting only new ones. All jobs found for a delivered company enter history, including those beyond the displayed 50. Companies use a separate monitor key in indeed-jobs-monitor. History is saved every 10 delivered companies and at the end. Restart recovery skips company keys already in the current dataset and restores their job history.
In companies mode, RUN_SUMMARY also includes output, companiesFound, companiesDelivered, companyPagesFetched, companyPagesFailed, companiesWithoutPage, ownerEmailRequested, ownerEmailVerified, ownerEmailProbable, ownerEmailUnknown, and relayAvailable. The dataset offers a Hiring companies view.
Use cases
- Recruiting feeds: collect fresh roles for an ATS or spreadsheet.
- Job boards: collect public titles, companies and optional full descriptions.
- Hiring signals: watch companies hiring for roles relevant to your product.
- Salary research: compare published pay ranges without guessing missing salaries.
Integrations
Call the actor through the Apify API:
curl -X POST "https://api.apify.com/v2/acts/agnes.developer.queen~indeed-jobs-scraper/run-sync-get-dataset-items" \-H "Authorization: Bearer $APIFY_TOKEN" \-H "Content-Type: application/json" \-d '{"keywords":"data engineer","location":"Remote","country":"us","maxJobs":20}'
Python:
import osfrom apify_client import ApifyClientclient = ApifyClient(os.environ["APIFY_TOKEN"])run = client.actor("agnes.developer.queen/indeed-jobs-scraper").call(run_input={"keywords": "data engineer", "location": "Remote", "maxJobs": 20})for job in client.dataset(run["defaultDatasetId"]).iterate_items():print(job["title"], job["companyName"], job["url"])
Node.js:
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: process.env.APIFY_TOKEN });const run = await client.actor('agnes.developer.queen/indeed-jobs-scraper').call({keywords: 'data engineer', location: 'Remote', maxJobs: 20,});const { items } = await client.dataset(run.defaultDatasetId).listItems();console.log(items.length, 'jobs');
For Make, use the Apify app to run agnes.developer.queen/indeed-jobs-scraper, then fetch its dataset items. In n8n, use the Apify integration or an HTTP Request node with the API example above. In Zapier, use Apify to run the actor and trigger your next step when the run succeeds. Map the job fields to Google Sheets, Airtable or your CRM.
FAQ
Can it scrape Indeed jobs without login?
Yes. It reads public Indeed pages without an Indeed account, cookies or login.
Does it return every full description?
Descriptions default to on. They succeeded for 63 of 63 jobs across the reviewed Chicago and London platform runs. Availability can change. A missing description remains null and the row uses the $0.001 job event.
Does it work outside the US?
The actor supports the US, UK, Germany, Australia and Canada. The reviewed London platform run returned 15 of 15 descriptions. Results depend on the source and the search.
Why did the run stop before maxJobs?
Indeed only exposes the first page without login. Variants may return the same jobs, and filters or monitor history can remove candidates. A search stops at its variant cap, after three consecutive variants with fewer than three new source jobs each, or at a login wall. The global job cap, spending limit and run deadline also stop collection. RUN_SUMMARY records the reason.
Can I use my own search URL?
Yes. Supply up to 20 supported Indeed /jobs URLs in searchUrls. Filters such as job type, salary, remote status and posting age stay in place. Added constraints only narrow the search; changing sort order can surface different matching jobs.
How do I get only new jobs?
Set onlyNew to true, save the input as a task and schedule it. The first run establishes history. Later runs skip job IDs already delivered for that search and title filter configuration within 90 days.
Support
Open the actor's Issues tab in Apify Console. Include the input, run ID, RUN_SUMMARY and DIAGNOSTICS. Do not include API tokens or proxy credentials.