Scraper for ZipRecruiter — Jobs, Pay & Benefits
Pricing
from $2.50 / 1,000 results
Scraper for ZipRecruiter — Jobs, Pay & Benefits
Export ZipRecruiter job listings by keyword and location: title, company, structured pay (min/max, period, annualised, employer vs estimate), benefits, remote/on-site, county and ZIP, 1-Click Apply flag and post dates. Optional full descriptions. No login. Not affiliated with ZipRecruiter.
Pricing
from $2.50 / 1,000 results
Rating
0.0
(0)
Developer
Logiover
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
10 days ago
Last modified
Categories
Share
Independent tool — not affiliated with, endorsed by, or sponsored by ZipRecruiter, Inc. "ZipRecruiter" is a trademark of its owner and is used here only to describe the public website this Actor reads.
ZipRecruiter Jobs Scraper exports job listings from ziprecruiter.com by keyword and location — with structured pay (min / max / period / annualised, and where the number came from), benefits, remote / on-site / hybrid, location down to county and ZIP code, employment type, 1-Click Apply flag, company, and posting / refresh dates. Give it a list of keywords and a list of locations and it searches every combination, walks every result page, removes duplicates and hands you a clean dataset you can export to Excel, CSV, JSON, Google Sheets or pull through the API.
No ZipRecruiter account, no login, no API key. It reads the same public search pages a job seeker sees.
✨ What you get from every job
- Job — title, ZipRecruiter job ID, canonical job URL, apply URL, short description, "new" and "urgently hiring" labels
- Company — name, ZipRecruiter company ID, company jobs page, logo, worker-survey highlights ("Respectful managers", "Good training"…)
- Pay — the text exactly as shown (
$24 - $28/hr), plus numeric min / max, pay period (hour, day, week, month, year), annualised min / max, currency and a pay source column that tells you whether the employer published it, it was read from the job description, or it is a ZipRecruiter estimate - Location — city, county, state, state code, ZIP code, country, plus workplace type (On-site, Remote, Hybrid) and an
isRemoteflag - Terms — employment type (Full-time, Part-time, Contractor, Temporary, Per diem), benefits (Medical, Dental, Vision, Life, Retirement, PTO)
- Timing — original posting date, last refresh date, "posted 3 days ago" text, last update timestamp, active flag
- Apply — whether the job takes ZipRecruiter 1-Click Apply or sends you to the employer's site
- Optional deep mode — full job description, industry, O*NET occupation code, education requirement, closing date, company website, company description and employer rating (1–10)
🎯 Who uses ZipRecruiter data and why
- Staffing agencies & recruiters — lead generation. Every row is a company that is hiring right now, with the role, the location and often the pay. Filter by city and role, then reach out to the hiring company before your competitors do.
- Compensation & HR analysts — pay benchmarking. Annualised min/max per role and city, with the pay source column so you can keep only employer-published figures for a clean benchmark.
- Labour-market research. Track posting volume, remote share, contract vs full-time mix, and benefits coverage by occupation and metro, week after week.
- Job boards & aggregators. Feed niche boards and newsletters with fresh, de-duplicated listings and canonical links.
- Sales intelligence. Hiring is a buying signal: a company hiring 20 truck drivers or 10 nurses is growing. Pair the
companycolumn with Company Firmographics Scraper or Website Contact Scraper to reach decision makers.
🚀 How to scrape ZipRecruiter jobs
- Enter one or more Keywords (job titles or skills) — e.g.
registered nurse,truck driver,python developer. - Enter one or more Locations —
City, ST, a state code (TX), a ZIP code (78701),RemoteorUnited States. Leave it empty to search the whole United States. - Optionally narrow it down: posted within, employment type, remote only, 1-Click Apply only, minimum salary, radius.
- Set Max jobs and press Start. Each keyword is searched in each location; the log names every search and how many listings ZipRecruiter reported for it.
- Download the results from the Storage / Dataset tab, or connect them to Google Sheets, Zapier, Make or a webhook.
📥 Input examples
Nurses in two cities, posted in the last 5 days
{"keywords": ["registered nurse", "LPN"],"locations": ["Houston, TX", "Dallas, TX"],"postedWithin": "5","maxItems": 2000}
Remote software jobs paying $150k+ anywhere in the US
{"keywords": ["software engineer", "data engineer", "devops engineer"],"locations": ["United States"],"remoteOnly": true,"minSalary": 150000,"maxItems": 3000}
Your own ZipRecruiter search URLs, with full descriptions and company websites
{"startUrls": [{ "url": "https://www.ziprecruiter.com/jobs-search?search=electrician&location=Phoenix%2C+AZ&days=10" }],"fetchDetails": true,"maxItems": 200}
All input fields:
| Field | What it does |
|---|---|
keywords | Job titles or skills. Each one is searched in each location. Empty = all jobs in the locations. |
locations | City, ST, state code, ZIP, Remote or United States. Empty = whole US. |
startUrls | ZipRecruiter search URLs (filters in the URL are kept). |
radius | 5, 10, 25 (default), 50 or 100 miles around each location. |
postedWithin | Any time, last 24 hours, 5, 10 or 30 days. |
employmentType | Any, Full-time, Part-time, Contract, Temporary. |
remoteOnly | Only jobs tagged remote. |
quickApplyOnly | Only jobs that take ZipRecruiter 1-Click Apply. |
minSalary | Only jobs whose annualised pay range reaches this amount. |
maxItems | Total unique jobs to collect (0 = no limit). |
maxPagesPerSearch | Cap pages (20 jobs each) per keyword × location (0 = all). |
fetchDetails | Open every job page for the full description, industry, O*NET code, education, company website and rating. |
concurrency | Result pages loaded in parallel (default 40, max 50). |
proxyConfiguration | Keep the default UNBLOCKER group — ZipRecruiter refuses ordinary proxies. |
Every filter offered here was checked against the live site and changes the result set. Filters that ZipRecruiter ignores (for example hybrid-only or internship) are deliberately not offered.
📤 Output example
A real row from a test run (truck driver jobs in Chicago, IL):
{"jobId": "66a23d0a69f03797","listingKey": "xMCft84-e40fJGTzftEeJQ","title": "Delivery Driver Non CDL","company": "The Chefs' Warehouse","companyId": "0+yOssKc5YiowTxF6XFpdZJQNLQ=","companyUrl": "https://www.ziprecruiter.com/co/The-Chefs'-Warehouse/Jobs","companyLogo": "https://www.ziprecruiter.com/svc/fotomat/public-nosensitive-ziprecruiter-logos/company/27f017ff.jpeg","location": "Chicago, IL","city": "Chicago","county": "Cook","state": "Illinois","stateCode": "IL","postalCode": "60608","country": "United States","countryCode": "US","locationType": "On-site","isRemote": false,"employmentType": "Full-time","payText": "$24 - $28/hr","payMin": 24,"payMax": 28,"payPeriod": "hour","payCurrency": "USD","payMinAnnual": 48000,"payMaxAnnual": 56000,"paySource": "Employer","payIsEstimated": false,"benefits": "Medical, Dental, Life, Retirement, PTO","workplaceHighlights": "Good employer, Recommended by parents, Respectful managers, Uninterrupted breaks","isNew": false,"isUrgent": false,"postedAt": "2026-10-02T19:56:02.000Z","refreshedAt": "2026-10-02T19:56:02.000Z","postedAgo": "7 days ago","updatedAt": "2026-10-10T10:47:40.353Z","isActive": true,"shortDescription": "About The Chefs' Warehouse The Chefs' Warehouse, Inc. ( combines exceptional quality and brand building with broadline food service distribution for more than 40 years. With Protein, Specialty,","isQuickApply": true,"applyType": "ZipRecruiter 1-Click Apply","applyUrl": "https://www.ziprecruiter.com/job-redirect?match_token=Cp8BChZ4TUNmdDg0LWU0MGZKR1R6ZnRFZUpREiQwMWExMjcxNy0wNmIyLTc3YjAtOGQ3MC0xNWUzMjMzMTIwMjcaW0FBRTVtSDVxS0NFTXFtSTNqLVhxR1JnXzNiVjV5czhnVmQzVHRYQmlGam9OTDJwMVdNT0o3STUzYkpqQlFRRTctN29rbXNXaHRNYVd1VG5XQ0IyeVpmVmU3b00gqNgCEAEYqNgC","jobUrl": "https://www.ziprecruiter.com/c/The-Chefs-Warehouse/Job/Delivery-Driver-Non-CDL/-in-Chicago,IL?jid=66a23d0a69f03797","searchKeyword": "truck driver","searchLocation": "Chicago, IL","searchTotalResults": 519,"page": 3,"position": 60,"scrapedAt": "2026-10-10T18:33:15.501Z"}
All output fields
| Field | Type | Description |
|---|---|---|
jobId | string | ZipRecruiter's job ID (the jid in the job URL). Stable across searches; used for de-duplication. |
listingKey | string | ZipRecruiter's listing key for this job card. |
title | string | Job title as posted. |
company | string | Hiring company name. |
companyId | string | ZipRecruiter's company identifier. |
companyUrl | string | ZipRecruiter page listing all jobs from this company. |
companyLogo | string | Logo image URL, when the company has one. |
location | string | Location as displayed, e.g. "Austin, TX". |
city | string | City of the job. |
county | string | County of the job, when resolved. |
state | string | State name. |
stateCode | string | Two-letter state code. |
postalCode | string | ZIP code, when the posting is pinned to one. |
country | string | Country name. |
countryCode | string | Two-letter country code. |
locationType | string | On-site, Remote, Hybrid or Remote optional, as ZipRecruiter displays it. |
isRemote | boolean | True when the job is tagged remote. |
employmentType | string | Full-time, Part-time, Contractor, Temporary, Per diem…; empty when the employer did not say. |
payText | string | Pay exactly as shown on the job card, e.g. "$32 - $39/hr". Empty when ZipRecruiter shows no pay. |
payMin | number | Lower pay bound in the pay period below. |
payMax | number | Upper pay bound in the pay period below. |
payPeriod | string | hour, day, week, month or year. |
payCurrency | string | Pay currency (USD). |
payMinAnnual | number | Lower bound annualised by ZipRecruiter (hourly × 2,000, weekly × 50). |
payMaxAnnual | number | Upper bound annualised by ZipRecruiter. |
paySource | string | Where the pay figure comes from: Employer, Job description, ZipRecruiter estimate, or Not published (ZipRecruiter estimate). |
payIsEstimated | boolean | True when the pay figures are ZipRecruiter estimates rather than published by the employer. |
benefits | string | Benefits detected for the job, e.g. "Medical, Dental, Vision, Retirement, PTO". |
workplaceHighlights | string | Worker-survey highlights ZipRecruiter shows for the employer, e.g. "Respectful managers". |
isNew | boolean | True when ZipRecruiter labels the job as new. |
isUrgent | boolean | True when the employer marked the job urgent. |
postedAt | string | When the job was first posted (ISO 8601, UTC). |
refreshedAt | string | When the posting was last refreshed or reposted (ISO 8601, UTC). |
postedAgo | string | Relative age shown on the card, e.g. "3 days ago". |
updatedAt | string | When ZipRecruiter last updated the job record (ISO 8601). |
isActive | boolean | Whether the listing is still active. |
shortDescription | string | Description snippet from the job card (about 200 characters). |
isQuickApply | boolean | True when the job accepts ZipRecruiter 1-Click Apply; false when applying happens on the employer site. |
applyType | string | "ZipRecruiter 1-Click Apply" or "Employer website". |
applyUrl | string | ZipRecruiter's apply link for the job (redirects to the employer site for external jobs). |
jobUrl | string | Canonical ZipRecruiter job page. |
searchKeyword | string | Keyword of the search that found this job. |
searchLocation | string | Location of the search that found this job. |
searchTotalResults | number | Number of listings ZipRecruiter reported for that search. |
page | number | Result page the job was found on. |
position | number | Rank of the job in that search. |
scrapedAt | string | When this row was collected (ISO 8601). |
description | string | Full job description as plain text. Only with "Open every job page". |
industry | string | Industry of the posting. Only with "Open every job page". |
occupationalCategory | string | O*NET-SOC occupation code and title, e.g. "29-1141.00: Registered Nurses". Only with "Open every job page". |
validThrough | string | Date the posting is set to expire. Only with "Open every job page". |
education | string | Education requirement, e.g. "Associate degree (required)". Only with "Open every job page". |
companyWebsite | string | Employer's own website, when ZipRecruiter knows it. Only with "Open every job page". |
companyDescription | string | One-line company description. Only with "Open every job page". |
employerRating | number | Worker rating of the employer on a 1–10 scale (Breakroom survey). Only with "Open every job page". |
employerRatingCount | number | Number of worker responses behind the rating. Only with "Open every job page". |
Empty cells are honest: they mean ZipRecruiter did not publish that value for that job. A missing pay range is never turned into 0, and a missing benefit list is never guessed.
💵 About the pay columns
ZipRecruiter attaches a pay range to almost every job, but not every range comes from the employer. The paySource column keeps them apart:
paySource | Meaning | Share in our test runs |
|---|---|---|
Employer | The employer published the pay. | 42% |
Job description | ZipRecruiter read the pay from the description text. | 31% |
Not published (ZipRecruiter estimate) | The employer gave no pay; the numbers are ZipRecruiter's internal estimate, not shown on the card. | 21% |
ZipRecruiter estimate | A ZipRecruiter estimate that is shown on the card. | 5% |
payIsEstimated is true for the last two. For salary benchmarking, filter on payIsEstimated = false. payMinAnnual / payMaxAnnual use ZipRecruiter's own annualisation (hourly × 2,000, weekly × 50), so hourly, weekly and yearly jobs can be compared on one scale.
📊 How many results can I get?
ZipRecruiter caps every single search at its own result count — typically 400 to 900 listings per keyword × location (measured: "truck driver" in Dallas, TX reported 728; "registered nurse" in Chicago, IL 670; "software engineer" in Dallas, TX 454). Pages past that point come back empty, so volume comes from the grid:
| Grid | Searches | Typical yield |
|---|---|---|
| 1 keyword × 1 city | 1 | 400–900 jobs |
| 3 keywords × 1 city | 3 | ~1,500–2,000 jobs |
| 2 keywords × 2 cities | 4 | ~2,000–2,500 jobs |
| 10 keywords × 10 cities | 100 | ~40,000–60,000 jobs |
| 20 keywords × 50 metros | 1,000 | several hundred thousand listings |
Jobs that appear in more than one search (a remote job, a 50-mile overlap between two cities) are kept only once — de-duplication is by ZipRecruiter's job ID across the whole run. With a Max jobs limit and several searches, each search gets a fair share first, so three keywords give you a mix rather than whichever search answered first.
Measured on the Apify platform (October 2026):
| Run | Result | Time | Duplicates |
|---|---|---|---|
| 3 keywords × Dallas, TX | 1,200 unique jobs | 5 min 51 s | 0 |
| 2 keywords × Atlanta, GA + Chicago, IL | 1,500 unique jobs (430 / 356 / 366 / 366 per search) | 6 min 52 s | 0 |
| 2 keywords × Los Angeles, CA + Miami, FL (12 parallel) | 1,100 unique jobs (353 / 271 / 269 / 213 per search) | 4 min 3 s | 0 |
| 2 keywords × Houston, TX + Phoenix, AZ (default settings, after the speed update) | 1,000 unique jobs | 1 min 22 s | 0 |
| Store example input | 50 jobs | 35–41 s | 0 |
⚡ Speed and cost
- 1,000 jobs in about 80 seconds with the default settings — measured 81.6 s for 1,000 unique jobs (2 keywords × Houston, TX + Phoenix, AZ). The same input took 269 s before the speed update.
- Rows stream in as pages land. On large runs the first rows appear within ~5 s (dozens of pages start at once); a small run waits for the proxy's first answer, typically 20–30 s. The 50-job example run finishes in 35–40 s.
- How: 40 result pages are loaded at once and every page the limit needs is queued at the start, so no worker waits. ZipRecruiter is reached through Apify's unblocking proxy, which answers a page in 20–40 s and occasionally not at all — a page that is slow gets a duplicate request on a fresh connection and the first answer wins. The run stops the instant Max jobs is reached and cancels whatever is still loading.
- Platform usage about $0.06 per 1,000 jobs (proxy + compute), on top of the per-result price. The unblocking proxy bills only pages it delivers; duplicate requests that lose the race are cancelled before they are billed.
- Deep mode (
fetchDetails) opens one extra page per job, so it is much slower. Use it for a focused list (a few hundred jobs), not for bulk exports. - Memory: 512 MB is plenty — peak measured at 106 MB with 40 pages in parallel.
💡 Tips
- Daily monitoring: schedule the Actor with
postedWithin: "1"and the same keywords and locations; you get only the last 24 hours of postings each day. - Bigger lists: add metros instead of widening the radius — ten 25-mile searches return far more distinct jobs than one 100-mile search.
- Only real salaries: filter the dataset on
payIsEstimated = false. - Remote jobs: either set
remoteOnly: trueor useRemoteas a location. - Company lists: export the Companies hiring view — one row per job with company, logo, ZIP and the company's ZipRecruiter page.
- Paste a URL: anything you set up on ziprecruiter.com (filters included) works as a start URL.
❓ FAQ
Do I need a ZipRecruiter account or API key? No. The Actor reads public search result pages — no login, no cookies, no API key.
Is there an official ZipRecruiter API for this? ZipRecruiter's APIs are for partners and employers posting jobs; there is no open API for searching listings. This Actor fills that gap with a no-code export.
Why does it need the UNBLOCKER proxy? ZipRecruiter answers ordinary datacenter and residential proxies with HTTP 403. Apify's UNBLOCKER group gets through reliably; the Actor selects it automatically. If you remove it, the log warns you.
How fast is it? About 80 seconds for 1,000 jobs with the default settings; rows appear in the dataset as each page lands. Larger runs scale with the number of searches; raise Parallel requests (up to 50) for very large grids.
What happens if ZipRecruiter blocks a request?
A refused or failed page is retried on a fresh exit IP (up to four attempts). If nothing at all can be fetched, the run fails with the reason instead of reporting success with zero rows, so an empty result always means "no matching jobs", never "blocked". Every run writes a SUMMARY record (outcome, pages read, per-search counts) to the key-value store.
Why did a search return fewer jobs than ZipRecruiter's headline number? ZipRecruiter caps each search at its own result count and some listings are duplicates of jobs already collected from another search. Add more keywords or locations for more volume.
How accurate is the pay data?
The numbers are exactly what ZipRecruiter holds for the job. Use paySource / payIsEstimated to tell employer-published pay from ZipRecruiter estimates (see the pay section above).
Can I get the full job description? Yes — turn on Open every job page. It also adds industry, O*NET occupation, education requirement, closing date, company website and employer rating.
Can I scrape jobs outside the United States?
This Actor targets ziprecruiter.com (US listings). Use Remote as a location for remote roles open to US candidates.
How do I run it every day?
Create a Schedule in Apify Console with the same input and postedWithin: "1". Combine with a webhook or the Google Sheets integration to get new jobs delivered automatically.
Can I export to Excel, CSV or Google Sheets? Yes. Every dataset exports to JSON, CSV, Excel, XML, RSS and HTML, and connects to Google Sheets, Zapier, Make, n8n, Airtable and webhooks.
Will the same job appear twice?
No. Jobs are de-duplicated by ZipRecruiter's job ID across all searches in a run. Across separate runs, use jobId as your key.
Is scraping ZipRecruiter legal? The Actor only reads publicly available job listings, without logging in or bypassing any access control. Job postings are published to be seen. You are responsible for how you use the data — respect ZipRecruiter's terms, applicable law and privacy rules (GDPR/CCPA) where personal data is involved; job listings themselves rarely contain any.
🔌 Use it through the API
JavaScript (apify-client)
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });const run = await client.actor('logiover/ziprecruiter-jobs-scraper').call({keywords: ['registered nurse'],locations: ['Houston, TX', 'Dallas, TX'],postedWithin: '5',maxItems: 1000,});const { items } = await client.dataset(run.defaultDatasetId).listItems();console.log(items.length, items[0]);
Python (apify-client)
from apify_client import ApifyClientclient = ApifyClient("YOUR_APIFY_TOKEN")run = client.actor("logiover/ziprecruiter-jobs-scraper").call(run_input={"keywords": ["truck driver"],"locations": ["Atlanta, GA", "Chicago, IL"],"maxItems": 1500,})for job in client.dataset(run["defaultDatasetId"]).iterate_items():print(job["title"], job["company"], job["payText"])
HTTP (run synchronously and get the items)
curl -X POST "https://api.apify.com/v2/acts/logiover~ziprecruiter-jobs-scraper/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \-H "Content-Type: application/json" \-d '{"keywords":["electrician"],"locations":["Phoenix, AZ"],"maxItems":200}'
🔗 Related scrapers
- Indeed Jobs Scraper — job listings and salary data from Indeed
- LinkedIn Jobs Scraper — LinkedIn job postings, no login
- Built In Tech Jobs Scraper — startup and tech jobs
- Greenhouse Job Board Scraper — every open role from Greenhouse career pages
- Website Contact Scraper — emails and phones from the company websites you collect here
⚖️ Legal & trademark
This Actor is an independent tool and is not affiliated with, endorsed by, or sponsored by ZipRecruiter, Inc. or any of its subsidiaries. All trademarks mentioned are the property of their respective owners. The Actor collects publicly available information only; it does not log in, does not bypass access controls, and does not collect private user data. You are responsible for using the output in line with the website's terms and with applicable law.
💬 Support
Missing a field or hit a problem? Open an issue on the Actor's Issues tab and include the run ID — every run's SUMMARY record shows exactly what happened.
📝 Changelog
2026-10-10 — speed update
- Faster: 1,000 jobs in ~80 s instead of ~270 s. 40 pages in parallel by default (max 50), all pages for the limit queued up front, slow pages hedged with a duplicate request (first answer wins, the loser is cancelled and not billed), one spare page per search so the slowest page is never waited for, and an immediate stop with in-flight requests cancelled once Max jobs is reached.
- Rows stream into the dataset page by page; the status line shows live progress.
- Fixed: with many pages finishing at once, a run could save more rows than Max jobs. The limit is now exact.
2026-10-10
- First release: keyword × location grid, all verified ZipRecruiter filters, structured pay with source, benefits, workplace type, county/ZIP location, 1-Click Apply flag, posting and refresh dates.
- Reads both page formats ZipRecruiter serves (it switches between them at random).
- Fair share of the row limit per search; de-duplication across searches by job ID.
- Optional deep mode: full description, industry, O*NET occupation, education, closing date, company website, employer rating.
- Runs that cannot reach ZipRecruiter fail with the reason instead of returning an empty success.