ZipRecruiter Jobs Scraper - Advertised Pay avatar

ZipRecruiter Jobs Scraper - Advertised Pay

Pricing

from $0.75 / 1,000 results

Go to Apify Store
ZipRecruiter Jobs Scraper - Advertised Pay

ZipRecruiter Jobs Scraper - Advertised Pay

Searches ZipRecruiter by keyword and location and returns each listing with the employer's advertised pay as numbers, the pay interval and currency, employmentType, city, state, ZipRecruiter's own posted timestamp and the apply URL. Salary estimates are dropped, never passed off as real.

Pricing

from $0.75 / 1,000 results

Rating

0.0

(0)

Developer

String

String

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

14 days ago

Last modified

Share

What does ZipRecruiter Jobs Scraper do?

It runs a ZipRecruiter keyword search in a location and writes one row per listing on the results page, with the employer's advertised pay as numbers, the interval it is quoted over, the currency, the employment type, a split city and state, ZipRecruiter's own posted timestamp and the apply URL.

The distinction that matters here is advertised versus estimated. ZipRecruiter computes a salary estimate for postings that quote no pay and marks that block as not for display. This Actor drops it. salaryMin is null exactly when the employer named no figure, so any average you compute from this dataset is an average of what employers actually offered rather than a mixture of offers and predictions.

The rows are read out of the search page's React Server Components payload, not out of the schema.org markup beside it. That markup is an ItemList with a title and a link per job and nothing else. The payload carries pay as integers with an explicit interval and currency, the employer's posted date, a resolved city and state, and the employment type.

No ZipRecruiter account, cookie or API key is used. This is the search a logged-out visitor runs.

What data does it extract?

FieldTypeDescription
jobIdstringZipRecruiter's listing key. Stable, so use it to de-duplicate and to diff between runs
titlestringThe posting title
companystringThe employer name on the listing
salaryMinnumberAdvertised lower bound, for example 78000. null when the employer advertised no pay
salaryMaxnumberAdvertised upper bound. null for a flat rate, since one figure is a rate and not a range
salaryPeriodstringThe interval the pay is quoted over, for example year or hour
currencystringISO code, for example USD
employmentTypestringZipRecruiter's own type, lowercased, for example full_time or part_time
locationstringAs ZipRecruiter displays it, for example Austin, TX US
citystringAlready split out, so you can group without parsing
statestringAlready split out
postedAtstringISO 8601 timestamp from ZipRecruiter's own posted date. A real timestamp, not a day count
jobUrlstringDirect link to the ZipRecruiter posting
querystringThe search term this row came from, so a multi-query run stays attributable
sourceUrlstringThe search URL the row was read from
collectedAtstringISO 8601 timestamp of the run

Why scrape ZipRecruiter?

ZipRecruiter's index leans towards the parts of the labour market that technology-focused boards cover badly: hourly work, healthcare, trades, logistics, retail, regional employers. Combined with a real advertised-pay field and a real posted timestamp, that makes it a decent source for wage research rather than only for job discovery.

  • Benchmark advertised wages by role and metro, with no estimates diluting the sample.
  • Compare hourly against salaried demand for the same role using salaryPeriod.
  • Track how posted pay for a role moves over months, keyed on jobId and postedAt.
  • Build recruiting or sales lists by role, employer and metro.
  • Feed a niche job board with live postings and a stable link back to the source.
  • Measure how quickly listings for a role turn over, by diffing scheduled runs.

How to use it

  1. Put job titles or keywords in Search terms, one per entry. Up to 50 per run.
  2. Set Location to a city, a state or a ZIP code. It applies to every query in the run.
  3. Set Maximum results if you want a ceiling. The default is 1000.
  4. Start the run and export the dataset as JSON, CSV, Excel or XML, or read it over the API.
  5. To cover more of a market, add narrower search terms rather than expecting one term to paginate. Each query reads one results page.

Input

FieldTypeDefaultDescription
queriesarray of stringsrequiredJob titles or keywords. Between 1 and 50. Duplicates collapse to one fetch
locationstringUnited StatesCity, state or ZIP. One per run
maxItemsinteger1000Cap on dataset rows, up to 50,000
concurrencyinteger3Searches run in parallel, up to 10
{
"queries": ["registered nurse", "cdl driver", "warehouse associate"],
"location": "Austin, TX",
"maxItems": 1000,
"concurrency": 3
}

Output

One row per listing. Illustrative shape:

{
"jobId": "a1f3c9d27b4e6058",
"title": "Registered Nurse - ICU",
"company": "Ascension Seton",
"salaryMin": 78000,
"salaryMax": 96000,
"salaryPeriod": "year",
"currency": "USD",
"employmentType": "full_time",
"location": "Austin, TX US",
"city": "Austin",
"state": "TX",
"postedAt": "2026-08-14T11:02:37Z",
"jobUrl": "https://www.ziprecruiter.com/c/Ascension-Seton/Job/Registered-Nurse-ICU/-in-Austin,TX?jid=a1f3c9d27b4e6058",
"query": "registered nurse",
"sourceUrl": "https://www.ziprecruiter.com/jobs-search?search=registered+nurse&location=Austin%2C+TX",
"collectedAt": "2026-08-20T09:14:02.118Z"
}

A posting advertising a flat hourly rate comes back as

"salaryMin": 57, "salaryMax": null, "salaryPeriod": "hour"
. A posting that advertises no pay at all has all four compensation fields null, which is the honest answer rather than ZipRecruiter's estimate wearing the employer's name.

Each run also writes a SUMMARY record with the item count, the failure count and every search that could not be read, with its error.

How much does it cost?

Pay per event, charged once per result row written to the dataset. The current rate is on this Actor's pricing tab, and a search that fails writes no rows and costs nothing.

One search term is one page fetch, so a run's cost tracks the number of queries you send.

Runs started from an Apify free plan stop at 250 requests and 250 results and report that in the run status. Any paid plan runs the full input and your maxItems. The limit binds on requests as well as rows because these fetches go through our own infrastructure, which Apify does not reimburse for free-plan runs.

Using it with the Apify API

import { ApifyClient } from "apify-client";
const client = new ApifyClient({ token: "<YOUR_APIFY_TOKEN>" });
const run = await client.actor("usestring/ziprecruiter-jobs").call({
queries: ["registered nurse", "cdl driver"],
location: "Austin, TX"
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
const annual = items.filter((job) => job.salaryPeriod === "year" && job.salaryMin);
const median = annual.map((job) => job.salaryMin).sort((a, b) => a - b)[Math.floor(annual.length / 2)];
console.log(`${annual.length} listings advertise annual pay, median floor ${median}`);

The run's SUMMARY record, in the same run's key-value store, lists any search that failed.

This Actor reads public ZipRecruiter search result pages, the same ones an anonymous visitor gets. No account, no login, no cookie, no API key, and nothing behind a paywall or an authentication wall.

The output describes job listings and the employers that posted them. There are no candidate profiles, no resumes, no applicant data and no recruiter contact details anywhere in it.

Whether a particular use of the collected data is permitted depends on where you are and on the terms you have agreed to. This is background rather than legal advice, and it is worth reading ZipRecruiter's terms if your use case goes beyond research.

FAQ

Do I need a ZipRecruiter account or API key? No. Only public search pages are read, with no account, login, cookie or key.

Is the salary advertised or estimated? Advertised, always. ZipRecruiter's own estimate for postings that quote no pay is marked as not for display, and it is discarded here rather than passed off as the employer's figure. That is why salaryMin is often null, and it is the correct answer.

Why is salaryMax sometimes null when salaryMin is set? Because the posting quotes a flat rate such as $57 an hour. One figure is a rate, not a range, so the upper bound is left empty instead of repeating the lower one.

How many listings does one search return? Around 20, which is what one results page carries. This Actor reads the first page per query and does not paginate, so ten search terms come to roughly 200 listings. Narrower terms are how you widen coverage.

Can I search several locations in one run? No. One run searches one location across all of its queries. Start one run per location.

Can I get the full job description? No. The output is the fields ZipRecruiter exposes on the search results page. Follow jobUrl for the description.

How fresh are the results? They are fetched live, and postedAt is ZipRecruiter's own timestamp rather than a relative label, so you can filter on real recency instead of on a rounded day count.

Feedback

If a search returns nothing, or a pay figure looks wrong, open an issue from this Actor's Store page with the search term, the location and a link to the listing. Compensation formats are where the edge cases live, and a specific listing is what makes one fixable.