ZipRecruiter Scraper API - US Jobs, Salary & Apply URL avatar

ZipRecruiter Scraper API - US Jobs, Salary & Apply URL

Pricing

from $0.80 / 1,000 job returneds

Go to Apify Store
ZipRecruiter Scraper API - US Jobs, Salary & Apply URL

ZipRecruiter Scraper API - US Jobs, Salary & Apply URL

Scrape ZipRecruiter jobs: title, company, city, employment type, remote flag, benefits, exact posted date and the employer’s apply URL. Every row flags whether the pay is employer-published or a ZipRecruiter estimate. $0.80/1,000 jobs - the lowest of any established ZipRecruiter scraper.

Pricing

from $0.80 / 1,000 job returneds

Rating

0.0

(0)

Developer

Dami's Studio

Dami's Studio

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

6 hours ago

Last modified

Share

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.