ZipRecruiter Scraper
Scrape ZipRecruiter jobs by keyword, city, remote or salary. Exact posted dates, apply URLs, pay marked published or estimated. $0.80 per 1,000.
How it works
- 1Open it on Apify
Hit Run on Apify — it opens the tool in the cloud, no install.
- 2Set the inputs
Adjust
searchQueries,location,startUrls(sensible defaults are pre-filled). - 3Click Run
The tool runs on Apify’s cloud and collects the data for you.
- 4Export the results
Download as JSON, CSV or Excel, or pipe straight into your app, Google Sheets, or an AI agent.
Pricing
$0.0008 per job = $0.8 per 1,000
| You are charged for | When | Price |
|---|---|---|
| Job returned | Charged once per genuine ZipRecruiter posting: title, company, city and state, employment type, remote/hybrid flag, benefits, the exact ISO posted date, the employer apply URL, and pay with a source flag separating employer-published figures from ZipRecruiter estimates. Sample rows, diagnostics, duplicates, and rows dropped by the employer-published-pay filter are never charged. | $0.0008 |
Pay-per-event pricing: you are billed per result, not per subscription. Billing is handled by Apify on your own account. These are the live Apify store prices, in effect since 2026-08-09, and they are what you are actually charged.
Inputs
| Field | What it does | Type |
|---|---|---|
searchQueries | One search per entry, for example "registered nurse" or "warehouse associate". Leave empty and set a location to get every job in that place. | array |
location | A city and state ("Austin, TX"), a state ("Texas") or "United States". ZipRecruiter is a US board; ziprecruiter.ca and .co.uk do not serve this data. | string |
startUrls | Paste ZipRecruiter search URLs (https://www.ziprecruiter.com/jobs-search?search=...&location=...) or individual job pages (https://www.ziprecruiter.com/c/{Company}/Job/{Title}/-in-{City},{ST}?jid=...). Search filters in the URL are read and re-used. | array |
maxItems | Stop after this many jobs. You are charged only for jobs actually returned. | integer |
days | Only jobs posted in the last N days. ZipRecruiter accepts exactly these values; anything else is ignored by the site and silently returns the unfiltered list, so it is rejected here instead. | string |
radius | Distance from the location. Ignored for state-wide and country-wide locations. | string |
locationType | Restrict to on-site, remote or hybrid postings. Uses ZipRecruiter’s own location-type facet. | string |
employmentType | Restrict to one employment type. "As needed / per diem" is ZipRecruiter’s PRN bucket. | string |
experienceLevel | Restrict to one seniority band, as classified by ZipRecruiter. | string |
applyType | "1-Click Apply" restricts the search to postings that accept an application on ZipRecruiter itself. | string |
salaryMin | ZipRecruiter's own salary filter. It uses the site's pay figure, which for some postings is an estimate - see "Only employer-published pay" below if you need certainty. | integer |
salaryMax | ZipRecruiter’s salary ceiling filter, as a whole-dollar annual figure. | integer |
onlyEmployerPublishedSalary | Keep only postings whose pay came from the employer (a structured pay field, or pay written into the job description). Drops ZipRecruiter's own estimates. Dropped rows are NOT charged. About 76% of postings survive this filter. | boolean |
includeDescription | Adds the complete job description plus the employer's ZipRecruiter review score and review count. Costs one extra request per job, so a run takes roughly three times longer. Without it you still get a ~200-character summary. | boolean |
egressMode | Leave this on "auto" unless a run gets challenged repeatedly, then try another option. | string |
What you get
A structured dataset — each result includes fields like:
titlecompanyNamelocationTextemploymentTypeTextisRemotesalaryTextsalarySourcesalaryIsEstimateestimatedSalaryTextpostedAtpostedAtRelativebenefitsTextapplyUrlurlsalaryIsPublishedByEmployersalaryShownOnSiteAsEstimatesalaryMinsalaryMaxsalaryIntervalestimatedSalaryMinestimatedSalaryMaxExport every run as JSON, CSV or Excel, or send it to your app, a database, Google Sheets, or an AI agent.
Related tools in Job Market Scrapers
Other ready-to-run tools in the same category — all pay-per-use on the Apify cloud.
LinkedIn Company Employees Scraper
Find a company's employees on LinkedIn, no login. Give a company URL or name. Get name, headline, job title, location and profile URL. $1.80 per 1,000.
Indeed Jobs Scraper
Scrape Indeed jobs by keyword and location in 11 countries. Get company, salary, benefits, remote flag, employer rating and apply URL. $4.00 per 1,000.
Glassdoor Company Reviews Scraper
Scrape Glassdoor reviews: pros, cons, six sub-ratings, recommend rate and CEO approval. First page per company without login. $2.50 per 1,000.
StepStone Jobs Scraper
Scrape StepStone jobs in DE, AT, NL and BE by keyword and location. Get company, salary, posted date, remote flag and links. $3.00 per 1,000 jobs.
Remotive Jobs Scraper
Scrape remote jobs from Remotive by keyword or category: company, region, salary, tags and plain-text descriptions. $2.00 per 1,000 jobs.
Multi-ATS Jobs Scraper (Greenhouse, Lever, Ashby)
Scrape open jobs from Greenhouse, Lever and Ashby boards. Get the title, location, department, posted date, description and apply link. $3.00 per 1,000.
Where this tool sits
- Categories
- Job Market Scrapers
ZipRecruiter Scraper: US jobs with the apply URL and pay marked published or estimated
Search ZipRecruiter by title and place, or paste a search URL, and every job comes back as a row: title, company, city and state, employment type, remote or on-site, the exact posted date, and the employer's own apply link rather than a redirect.
The awkward part first, and it is the reason this exists. A lot of the pay on ZipRecruiter is ZipRecruiter's own estimate, not the employer's number. Rows keep the two apart: employer figures live in salaryMin and salaryMax, estimates live in estimatedSalaryMin and estimatedSalaryMax, and salarySource says exactly where the number came from. They are never mixed into one column.
| Input | Job titles or keywords with a US location, or ZipRecruiter URLs |
| Output | One row per job |
| Ceiling | 20,000 jobs per run. About 750 per search is ZipRecruiter's own limit |
| Account needed | None, and nothing to install |
| Price | $0.80 per 1,000 jobs, flat on every plan |
🏷️ What ZipRecruiter Scraper does
Give it keywords and a location, or paste ZipRecruiter search URLs and job pages. A pasted search URL keeps the filters already on it. Leave the keywords empty and set a location and you get everything in that place.
Filter values are checked before anything runs. ZipRecruiter ignores a value outside its own vocabulary and returns the unfiltered board at HTTP 200, so a typo would quietly get you every job in the country and bill you for it. Only values the site actually honours are accepted here, which is why the filters are pick-lists rather than free text.
onlyEmployerPublishedSalary is the strict version of the pay promise: it keeps only postings whose figure came from the employer, either as a structured pay field or written into the description, and drops ZipRecruiter's estimates. About 76% of postings survive it. Dropped rows never reach the dataset and are not billed.
includeDescription adds the full description plus the employer's ZipRecruiter review score and review count. It costs one extra request per job, so the run takes roughly three times as long. It adds fields rather than rows, so the price does not move. Without it you still get a summary of around 200 characters.
📥 What you give it
{
"searchQueries": ["registered nurse"],
"location": "Austin, TX",
"days": "5",
"locationType": "only_remote",
"employmentType": "full_time",
"maxItems": 200
}
| Field | Default | What it is |
|---|---|---|
searchQueries | box starts at registered nurse | One search per entry. Leave empty and set a location for everything there. |
location | box starts at Austin, TX | A city and state, a state, or United States. US board only. |
startUrls | none | ZipRecruiter search URLs or individual job pages, pasted from the site. |
days | any time | Posted within the last 1, 5, 10 or 30 days. |
radius | ZipRecruiter's own 25 miles | 5, 10, 25, 50 or 5000 miles. Ignored for state-wide and country-wide searches. |
locationType | any | no_remote, only_remote or hybrid. |
employmentType | any | Full-time, part-time, contract, contract to hire, temporary, per diem or other. |
experienceLevel | any | no_experience, junior, mid or senior, as ZipRecruiter classifies them. |
applyType | any | has_zipapply keeps only postings you can apply to on ZipRecruiter itself. |
salaryMin, salaryMax | none | ZipRecruiter's own annual salary filter, which uses its own pay figure. |
onlyEmployerPublishedSalary | off | Keep only employer-published pay. Dropped rows are not billed. |
includeDescription | off | Full description and the company's review score. |
maxItems | box starts at 50 | Total jobs for the run, up to 20,000. |
egressMode | auto | Leave it alone. The other settings are there for debugging. |
proxyConfiguration | leave it alone | Optional. Only if you want the run to go out through a particular network of your own. |
📤 What you get back
A real row from a run on 20 September 2026, with the long redirect token cut short:
{
"id": "EazrVIHe3SD2W9b_kEu68w",
"openSeatId": "e8a515fa6f1a5372",
"title": "RN, Registered Nurse - IMC MedSurgical A",
"url": "https://www.ziprecruiter.com/c/CHRISTUS-Health/Job/RN,-Registered-Nurse-IMC-MedSurgical-A/-in-San-Marcos,TX?jid=e8a515fa6f1a5372",
"companyName": "CHRISTUS Health",
"companyId": "christus health",
"companyUrl": "https://www.ziprecruiter.com/co/CHRISTUS-Health/Jobs",
"locationText": "San Marcos, TX",
"city": "San Marcos",
"state": "Texas",
"stateCode": "TX",
"postalCode": "78666",
"county": "Hays",
"countryCode": "US",
"locationTypes": ["IN_PERSON"],
"locationTypeText": "On-site",
"isRemote": false,
"isHybrid": false,
"isInPerson": true,
"employmentTypes": ["FULL_TIME"],
"employmentTypeText": "Full-time",
"salarySource": "ziprecruiter_internal_model",
"salaryIsEstimate": true,
"salaryIsPublishedByEmployer": false,
"salaryMin": null,
"salaryMax": null,
"salaryMinAnnual": null,
"salaryMaxAnnual": null,
"salaryInterval": null,
"salaryCurrency": "USD",
"salaryText": null,
"estimatedSalaryMin": 31.25,
"estimatedSalaryMax": 49.75,
"estimatedSalaryMinAnnual": 62500,
"estimatedSalaryMaxAnnual": 99500,
"estimatedSalaryInterval": "HOUR",
"salaryShownOnSiteAsEstimate": false,
"benefits": [],
"benefitsText": null,
"postedAt": "2024-02-27T20:43:20Z",
"repostedAt": "2026-09-14T20:43:20Z",
"postedAtRelative": "5 days ago",
"isReposted": false,
"isActive": true,
"isNew": false,
"updatedAt": "2026-09-19T22:49:08.815300215Z",
"applyType": "OPEN_EXTERNAL",
"hasZipApply": false,
"applyUrl": "https://careers.christushealth.org/opportunity/rn-registered-nurse-imc-medsurgical-a-325738?utm_campaign=ZipRecruiterPPC-RN",
"applyRedirectUrl": "https://www.ziprecruiter.com/job-redirect?match_token=Cp8BChZFYXpyVklIZTNTRDJXOWJfa0V1Njh3...",
"shortDescription": "Description CHRISTUS Santa Rosa Hospital - San Marcos, which originated in 1923 as Hays County Soldiers, Sailors and Marines Memorial Hospital, opened at its current Wonder World Drive location in",
"description": null,
"companyRating": null,
"companyReviewCount": null,
"searchQuery": "registered nurse",
"searchLocation": "Austin, TX",
"searchUrl": "https://www.ziprecruiter.com/jobs-search?search=registered+nurse&location=Austin%2C+TX",
"page": 1,
"position": 1,
"scrapedAt": "2026-09-20T07:08:28.109Z"
}
| Field | What it is |
|---|---|
salarySource | employer_provided, employer_description_parsed, ziprecruiter_estimate, ziprecruiter_internal_model or none. The first two are the employer's number. |
salaryIsPublishedByEmployer / salaryIsEstimate | The same split as two booleans, for filtering in a spreadsheet. |
salaryShownOnSiteAsEstimate | Whether ZipRecruiter labels it as an estimate on its own page, which is not always the same thing. |
applyUrl | The employer's own application page, decoded out of ZipRecruiter's redirect. applyRedirectUrl keeps the redirect if you want it. |
postedAt / repostedAt / postedAtRelative | The original posting date, when it was put back up, and ZipRecruiter's own wording. A row can carry an old postedAt and a recent repostedAt. |
description, companyRating, companyReviewCount, companyWebsite, companyIndustry, companyHq | Only with includeDescription on. |
searchQuery, searchLocation, searchUrl, page, position | Which search produced the row, and the exact URL used. |
Two dataset views are set up in the Console: overview, and salaryHonesty, which puts the published and estimated pay columns side by side.
🧾 Reading the output
| Row | How to spot it | Billed |
|---|---|---|
| A job | It has an id and no ok key | yes |
| The sample row | _sample: true and charged: false | no |
| A diagnostic | ok: false, _diagnostic: true and an errorCode | no |
Job rows carry no charged field. The flag is only on the sample and diagnostic rows, to mark them as not real results. The sample row looks like a realistic posting in the Console table, so filter on _sample rather than reading by eye.
| Code | What it means |
|---|---|
NO_RESULTS | That search matched nothing. |
BAD_URL | A link that is not a ZipRecruiter search or job page. |
JOB_UNAVAILABLE | A pasted job has expired or been taken down. |
UNKNOWN_FILTER_VALUE | A filter value ZipRecruiter does not honour, refused before the search ran. |
SEARCH_PAGE_UNAVAILABLE | A page of results did not come back. |
▶️ How to run it
1. Open ZipRecruiter Scraper and click Try for free. 2. Type a role into Job titles or keywords and a place into Location. 3. Set Posted within, On-site / remote and the other filters if you want to narrow it. 4. Set Maximum jobs. Start around 25 to see what the rows look like. 5. Click Start, then download the dataset as JSON, CSV or Excel, or read it from the Apify API.
💰 How much does it cost?
$0.80 per 1,000 jobs. Flat on every Apify plan, with no volume tiers.
One billed row per job delivered. The sample row is not billed, diagnostic rows are not billed, a posting dropped by the employer-published-pay filter never reaches the dataset and is not billed, and includeDescription adds fields rather than rows so it does not change the price.
💡 What people use it for
- Pay benchmarking that is actually defensible, because estimates and employer figures sit in
separate columns.
- Recruiting-market research: who is hiring for a role in a metro, and how the postings are worded.
- Feeding an applicant tool with
applyUrl, which points at the employer rather than a redirect. - Watching a role daily with Posted within at 24 hours and diffing on
id. - Remote-market tracking, using
locationTypeand theisRemoteandisHybridflags.
🚧 What it does not do
- United States only. The Canadian and UK ZipRecruiter sites do not serve this data.
- About 750 jobs per search is ZipRecruiter's own depth. Split by city, role or date window to
go wider rather than raising the cap.
- No estimate dressed up as a salary. If the employer published nothing,
salaryMinand
salaryMax stay null and the estimate stays in its own fields.
- Full descriptions need the extra fetch, and even then not every posting carries one. Around
four in five do.
- Company ratings need that fetch too. Without it
companyRatingandcompanyReviewCountare
null.
- A single failed page of results is skipped and the run carries on, without a row in the
dataset marking the gap. Compare the count you asked for against the count you got.
postedAtcan be years old on a reposted job. ReadrepostedAtalongside it.
🧭 Which jobs scraper do you need?
| If you want | Use |
|---|---|
| US jobs with pay marked published or estimated | This one |
| Startup jobs from Wellfound | Wellfound Jobs Scraper |
| Jobs in Germany, Austria and Switzerland | XING Jobs Scraper |
| Jobs in Germany, Austria, the Netherlands and Belgium | StepStone Jobs Scraper |
| Internships and fresher jobs in India | Internshala Scraper |
❓ Questions people ask
How do I get only real employer salaries? Turn on Only employer-published pay. Roughly three in four postings survive it, and the ones dropped are not billed.
What is the difference between the salary fields? salaryMin and salaryMax are the employer's own figure. estimatedSalaryMin and estimatedSalaryMax are ZipRecruiter's model. A row never puts one in the other's place.
Does the apply link go to the employer? Yes. applyUrl is the employer's page, decoded out of ZipRecruiter's redirect. applyRedirectUrl keeps the original if you need it.
Why is description empty? Turn on Fetch full description and company rating. It triples the run time, which is why it is off by default.
Can I paste a search I set up on the site? Yes. The filters on the URL are read and reused.
Is this legal? These are public job postings. Apify's write-up on scraping and the law is a good starting point, and we are not lawyers.
🆘 If something breaks
Open the Issues tab on the actor page. Send the search or URL you used and the run ID. The errorCode on the diagnostic row usually names the problem on its own.