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 in the US. Some employers post their own salary and some let ZipRecruiter estimate one. Every row says which. You also get the title, company, city, employment type and remote flag. Rows carry the exact posted date and the apply URL. $0.80 per 1,000 jobs.

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

2

Monthly active users

a day ago

Last modified

Share

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.

InputJob titles or keywords with a US location, or ZipRecruiter URLs
OutputOne row per job
Ceiling20,000 jobs per run. About 750 per search is ZipRecruiter's own limit
Account neededNone, 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
}
FieldDefaultWhat it is
searchQueriesbox starts at registered nurseOne search per entry. Leave empty and set a location for everything there.
locationbox starts at Austin, TXA city and state, a state, or United States. US board only.
startUrlsnoneZipRecruiter search URLs or individual job pages, pasted from the site.
daysany timePosted within the last 1, 5, 10 or 30 days.
radiusZipRecruiter's own 25 miles5, 10, 25, 50 or 5000 miles. Ignored for state-wide and country-wide searches.
locationTypeanyno_remote, only_remote or hybrid.
employmentTypeanyFull-time, part-time, contract, contract to hire, temporary, per diem or other.
experienceLevelanyno_experience, junior, mid or senior, as ZipRecruiter classifies them.
applyTypeanyhas_zipapply keeps only postings you can apply to on ZipRecruiter itself.
salaryMin, salaryMaxnoneZipRecruiter's own annual salary filter, which uses its own pay figure.
onlyEmployerPublishedSalaryoffKeep only employer-published pay. Dropped rows are not billed.
includeDescriptionoffFull description and the company's review score.
maxItemsbox starts at 50Total jobs for the run, up to 20,000.
egressModeautoLeave it alone. The other settings are there for debugging.
proxyConfigurationleave it aloneOptional. 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"
}
FieldWhat it is
salarySourceemployer_provided, employer_description_parsed, ziprecruiter_estimate, ziprecruiter_internal_model or none. The first two are the employer's number.
salaryIsPublishedByEmployer / salaryIsEstimateThe same split as two booleans, for filtering in a spreadsheet.
salaryShownOnSiteAsEstimateWhether ZipRecruiter labels it as an estimate on its own page, which is not always the same thing.
applyUrlThe employer's own application page, decoded out of ZipRecruiter's redirect. applyRedirectUrl keeps the redirect if you want it.
postedAt / repostedAt / postedAtRelativeThe 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, companyHqOnly with includeDescription on.
searchQuery, searchLocation, searchUrl, page, positionWhich 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

RowHow to spot itBilled
A jobIt has an id and no ok keyyes
The sample row_sample: true and charged: falseno
A diagnosticok: false, _diagnostic: true and an errorCodeno

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.

CodeWhat it means
NO_RESULTSThat search matched nothing.
BAD_URLA link that is not a ZipRecruiter search or job page.
JOB_UNAVAILABLEA pasted job has expired or been taken down.
UNKNOWN_FILTER_VALUEA filter value ZipRecruiter does not honour, refused before the search ran.
SEARCH_PAGE_UNAVAILABLEA 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 locationType and the isRemote and isHybrid flags.

🚧 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, salaryMin and 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 companyRating and companyReviewCount are 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.
  • postedAt can be years old on a reposted job. Read repostedAt alongside it.

🧭 Which jobs scraper do you need?

If you wantUse
US jobs with pay marked published or estimatedThis one
Startup jobs from WellfoundWellfound Jobs Scraper
Jobs in Germany, Austria and SwitzerlandXING Jobs Scraper
Jobs in Germany, Austria, the UK, the Netherlands and BelgiumStepStone Jobs Scraper
Internships and fresher jobs in IndiaInternshala 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.