Request a tool
All toolsGuidesMCP serverRequest a toolPlatformsCategories
ZipRecruiter Scraper icon

ZipRecruiter Scraper

Scrape ZipRecruiter jobs by keyword, city, remote, employment type, salary or posting age.

32 runs on Apify
Run this in the cloudRun on Apify →

Job Market Scrapers

How it works

  1. 1
    Open it on Apify

    Hit Run on Apify — it opens the tool in the cloud, no install.

  2. 2
    Set the inputs

    Adjust searchQueries, location, startUrls (sensible defaults are pre-filled).

  3. 3
    Click Run

    The tool runs on Apify’s cloud and collects the data for you.

  4. 4
    Export the results

    Download as JSON, CSV or Excel, or pipe straight into your app, Google Sheets, or an AI agent.

Inputs

FieldWhat it doesType
searchQueriesOne search per entry, for example "registered nurse" or "warehouse associate". Leave empty and set a location to get every job in that place.array
locationA 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
startUrlsPaste 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
maxItemsStop after this many jobs. You are charged only for jobs actually returned.integer
daysOnly 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
radiusDistance from the location. Ignored for state-wide and country-wide locations.string
locationTypeRestrict to on-site, remote or hybrid postings. Uses ZipRecruiter’s own location-type facet.string
employmentTypeRestrict to one employment type. "As needed / per diem" is ZipRecruiter’s PRN bucket.string
experienceLevelRestrict 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
salaryMinZipRecruiter'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
salaryMaxZipRecruiter’s salary ceiling filter, as a whole-dollar annual figure.integer
onlyEmployerPublishedSalaryKeep 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
includeDescriptionAdds 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"auto" uses the plain Apify container address and falls back to Apify datacenter proxy on a block. Change this only if a run gets challenged repeatedly.string

What you get

A structured dataset — each result includes fields like:

titlecompanyNamelocationTextemploymentTypeTextisRemotesalaryTextsalarySourcesalaryIsEstimateestimatedSalaryTextpostedAtpostedAtRelativebenefitsTextapplyUrlurlsalaryIsPublishedByEmployersalaryShownOnSiteAsEstimatesalaryMinsalaryMaxsalaryIntervalestimatedSalaryMinestimatedSalaryMax

Export 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 iconJob Market Scrapers

LinkedIn Company Employees Scraper

Scrape LinkedIn company employees without a login or cookies. Company URL or name in; employee name, headline, job title, location and profile URL out.

Ready to run — no setup

Indeed Jobs Scraper iconJob Market Scrapers

Indeed Jobs Scraper

Scrape Indeed jobs by keyword and location across 11 countries. Get title, company, salary, remote flag, posted date, and apply URL as structured data.

16 use cases

Glassdoor Company Reviews Scraper iconJob Market Scrapers

Glassdoor Company Reviews Scraper

Scrape Glassdoor company reviews and ratings by company name or URL. Get pros, cons, recommend rate and CEO approval as rows for analysis.

20 use cases

StepStone Jobs Scraper iconJob Market Scrapers

StepStone Jobs Scraper

Scrape StepStone jobs across Germany, Austria, UK, NL and Belgium by keyword and location. Get company, salary, posted date, remote flag and apply links.

4 use cases

Remotive Jobs Scraper iconJob Market Scrapers

Remotive Jobs Scraper

Scrape remote jobs from Remotive by keyword and category. Get title, company, location, salary, tags and clean descriptions. No login or key.

3 use cases

Multi-ATS Jobs Scraper (Greenhouse, Lever, Ashby) iconJob Market Scrapers

Multi-ATS Jobs Scraper (Greenhouse, Lever, Ashby)

Scrape open jobs from Greenhouse, Lever, and Ashby boards by company. Get title, location, department, and apply link as JSON, CSV, or Excel. No key needed.

3 use cases

See all Job Market Scrapers →

ZipRecruiter Scraper API

Pulls job postings off ZipRecruiter — title, company, city and state, employment type, remote flag, benefits, the exact posting date, the employer's real apply URL, and pay with a flag saying where the pay figure came from. No login, no ZipRecruiter account, no API key.

Search by keyword and location, filter the way the site does, or paste ZipRecruiter URLs.

---

The pay problem, and what this Actor does about it

ZipRecruiter shows a salary on most job cards. A lot of those salaries were never written by the employer — they are ZipRecruiter's own model output. The site is upfront about this internally: every pay figure it serves carries a source label. Most scrapers throw that label away and hand you one salary column that silently mixes the two.

Measured on 1,413 postings returned by three real runs of this Actor (a ten-keyword national search, a ten-keyword Chicago search, and one nurse search in New York paged to exhaustion):

Where the pay figure came fromShareWhat this Actor does with it
Employer filled in a pay field49.0%salaryMin / salaryMax, salarySource: employer_provided
Employer wrote pay in the job description18.5%salaryMin / salaryMax, salarySource: employer_description_parsed
ZipRecruiter estimate, shown on the site labelled "Estimated"4.6%estimatedSalaryMin / estimatedSalaryMax, salaryIsEstimate: true
ZipRecruiter model output the site never displays at all27.7%estimatedSalaryMin / estimatedSalaryMax, salarySource: ziprecruiter_internal_model
No pay figure anywhere0.1%all pay fields null

So 67.5% of postings have pay the employer actually stated, and 32.3% carry a machine-generated number. Of the 392 rows in that last category, 392 of 392 — every single one — had no pay text on the ZipRecruiter page at all. The number exists only in the page's data payload.

99.9% of postings carry some pay number. A scraper that dumps them all into one salary column reports "99.9% salary coverage". Two thirds of that is real.

The two figures are never merged. salaryMin/salaryMax are employer figures only. Estimates live in estimatedSalaryMin/estimatedSalaryMax. Tick Only employer-published pay and the estimates are dropped before anything is charged — you do not pay for rows you discard. (Verified: a 25-job run with that box ticked saw 33 postings, returned 25, and was billed for 25.)

The split moves a lot with the search. Across the three runs the employer-published share was 57.7% (national, ten keywords), 68.3% (Chicago, ten keywords) and 70.5% (New York nursing). Check salarySource on your own run rather than assuming.

---

What you get per job

id, openSeatId, title, url

companyName, companyId, companyUrl, companyLogo

locationText, city, state, stateCode, postalCode, county, country, countryCode

locationTypes, locationTypeText, isRemote, isHybrid, isInPerson

employmentTypes, employmentTypeText

salarySource, salaryIsEstimate, salaryIsPublishedByEmployer, salaryMin, salaryMax, salaryMinAnnual, salaryMaxAnnual, salaryInterval, salaryCurrency, salaryText, estimatedSalaryMin, estimatedSalaryMax, estimatedSalaryMinAnnual, estimatedSalaryMaxAnnual, estimatedSalaryInterval, estimatedSalaryText, salaryShownOnSiteAsEstimate

benefits, benefitsText

postedAt, repostedAt, postedAtRelative, isReposted, isActive, isNew, updatedAt

applyType, hasZipApply, applyUrl, applyRedirectUrl

shortDescription, and with Fetch full description and company rating turned on: description, descriptionHtml, companyRating, companyRatingScale, companyReviewCount, companyDescription, companyWebsite, companyIndustry, companyHq

searchQuery, searchLocation, searchUrl, page, position, scrapedAt

Two fields worth calling out

postedAt is a real timestamp, not "3 days ago". ZipRecruiter renders 29 days ago on the card; the payload behind it has 2026-02-11T00:49:45Z. Both are returned — postedAt for filtering, postedAtRelative for reference. repostedAt is separate, because a lot of postings are refreshed.

applyUrl is the employer's own URL, not a ZipRecruiter redirect. ZipRecruiter wraps the destination in a job-redirect?match_token= link; the token is base64 and the real URL is inside it, so this Actor unwraps it without following the redirect. You get https://jobs.bswhealth.com/us/en/job/26013231/LVN-Clinic-Float-PRN rather than a tracking hop. 39.3% of the 1,413 postings measured were hosted on an external ATS, and applyUrl resolved on 97.1% of those. The rest are 1-Click Apply jobs hosted on ZipRecruiter itself, where hasZipApply is true and applyRedirectUrl is the link. That external share swings by sector — 13% on a Denver dental search, 49% on a New York nursing search.

---

Filters

Everything here maps to a real ZipRecruiter facet, and every value was checked against the live site.

InputValues
days1, 5, 10, 30
radius5, 10, 25, 50, 5000 (= 100+ miles)
locationTypeon-site / remote only / hybrid only
employmentTypefull-time, part-time, contract, contract-to-hire, temporary, as-needed (per diem), other
experienceLevelno experience, junior, mid, senior
applyType1-Click Apply only
salaryMin / salaryMaxannual USD

Why the values are a fixed list and not free text. ZipRecruiter does not reject a filter value it does not recognise. days=zzz returns HTTP 200 and the entire unfiltered result set — 650 jobs instead of 72. A typo would quietly bill you for rows that do not match what you asked for. So the Actor checks each value against the site's own vocabulary first and refuses the ones that are not in it, with an uncharged diagnostic row telling you what was rejected.

---

Examples

Nursing jobs in Austin, posted in the last five days, employer pay only:

{

  "searchQueries": ["registered nurse"],

  "location": "Austin, TX",

  "days": "5",

  "onlyEmployerPublishedSalary": true,

  "maxItems": 200

}

Remote software work nationwide, with full descriptions and employer ratings:

{

  "searchQueries": ["software engineer", "backend engineer"],

  "location": "United States",

  "locationType": "only_remote",

  "includeDescription": true,

  "maxItems": 100

}

From a URL you already have open:

{

  "startUrls": ["https://www.ziprecruiter.com/jobs-search?search=welder&location=Houston%2C+TX&radius=50"]

}

---

What this does not do

  • US only. ziprecruiter.ca serves a different application that has none of this data, and

ziprecruiter.co.uk 404s on the search route. Do not expect Canadian or UK jobs.

  • About 750 jobs per search, not the whole board. ZipRecruiter stops paginating around page 41.

One real run — nurse jobs within 50 miles of New York — returned 753 unique postings against a claimed 813 matches, and then ran dry. Split by city, job title or days window to go wider; there is no way to page past the site's own limit.

  • Company rating is often absent, and it needs the detail fetch. It is not on the search card,

and it only exists for employers that have been reviewed — small businesses usually have none. On a 15-job dental run only 1 job had a rating. Turn on *Fetch full description and company rating* and expect a run roughly three times slower.

  • Full descriptions land on about 80% of jobs with the detail fetch on; the rest are postings

whose body ZipRecruiter does not serve to a logged-out visitor.

  • Employer ratings are on a 0–10 scale, not five stars — that is ZipRecruiter's own scale.

companyRatingScale says so on every row.

  • Some search pages come back empty and are retried. ZipRecruiter intermittently answers a

perfectly valid page with a 24 KB shell at HTTP 200. The Actor retries those, and only gives up after two consecutive failures. That is a site behaviour, not a block.

  • No résumés, no employer contact details, no applications submitted.

---

Cost

$0.0005 to start a run, then $0.80 per 1,000 jobs. A flat rate — no volume tiers to unlock and no plan gates, the same number on your first run as on your thousandth.

Sample rows, diagnostic rows, duplicate postings, failed pages, and rows dropped by *Only employer-published pay* are never charged.

Real runs: 5 jobs in 4.9 seconds; 400 jobs in 51 seconds; 753 jobs in 102 seconds.

---

FAQ

Is there an official ZipRecruiter API? There is a partner API, but it is not open to the public — api.ziprecruiter.com/jobs/v1 needs a key issued to approved partners and returns 502 without one. This Actor reads the same public search pages a browser does.

Do I need a ZipRecruiter account or login? No. Everything here is what a logged-out visitor sees.

How do I scrape ZipRecruiter jobs by keyword and city? Set searchQueries to the keyword and location to "City, ST". One search runs per keyword.

Can I get remote jobs only? Yes — set locationType to only_remote. Setting location to "Remote" does not do the same thing; ZipRecruiter treats that as a place name and returns very few results.

Are the salaries real? Two thirds are. Across 1,413 measured postings, 49.0% came from a pay field the employer filled in and 18.5% were written by the employer into the job description. The remaining 32.3% are ZipRecruiter's own model. Every row says which, in salarySource, and the two kinds never share a column. See the table at the top.

What is ziprecruiter_internal_model? A pay range ZipRecruiter calculated and did *not* display on the site. It is in the page payload but not on the page. It is returned here for completeness, in the estimatedSalary* fields only, and never as salaryMin/salaryMax.

Why is postedAt months old when the card says "3 days ago"? Because the card shows the repost date. postedAt is the original posting timestamp and repostedAt is when it was last refreshed — both come straight from ZipRecruiter's payload.

How many jobs can one search return? Around 750. A real run took a New York nursing search to exhaustion and got 753 unique postings out of a claimed 813. ZipRecruiter stops serving results past page 41 regardless of how many matches it claims. Use narrower searches — by city, title or posting age — to cover more ground.

Can I get the full job description? Yes, tick *Fetch full description and company rating*. It costs one extra request per job.

Does it work for company pages? Paste a specific job URL into startUrls and you get that job. Whole-company crawling is not supported yet; search by company name in searchQueries instead.

Is scraping ZipRecruiter legal? This Actor reads public job listings. You are responsible for how you use the data, including ZipRecruiter's terms and any applicable data-protection rules. It does not touch résumés, candidate profiles or anything behind a login.