ZipRecruiter Jobs Lookup from $0.05/search
Pricing
from $38.00 / 1,000 search with results
ZipRecruiter Jobs Lookup from $0.05/search
Run keyword + location job searches on ZipRecruiter through a real browser and get every posting on the first results page in one row: title, company, location, salary and apply link. No login required. You pay per search that finds results, and a search that finds nothing is free.
Pricing
from $38.00 / 1,000 search with results
Rating
0.0
(0)
Developer
Adrian Voss
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
Share
You write job searches the way you'd type them into ZipRecruiter —
python developer @ Austin TXNo login. No API key. ZipRecruiter's search page sits behind a Cloudflare challenge to a plain request, and even once that's cleared the listings are rendered client-side — so this actor drives a real headless browser through Apify's UNBLOCKER proxy rather than parsing raw HTML. See "Why this one" below.
Who it's for
Recruiters, staffing agencies, and outbound teams building a US candidate or lead list who need fresh postings by role and city without clicking through ZipRecruiter's search pages by hand. It's also the data layer behind an internal hiring-trends dashboard, or a Clay/n8n workflow that watches a role for new postings on a schedule.
This actor is one of several wide-net keyword + location job-search actors in this catalogue
(alongside seek-jobs-search-lookup for Australia and xing-jobs-lookup for the DACH region)
rather than a per-company listing. If you already know which companies to target,
company-domain-enrichment's hiring-activity source answers a different question — this one
answers "who's hiring for this role, in this US city" when you don't have the company list
yet.
Why this one
- Clears a real Cloudflare + client-rendering wall. A plain request to ZipRecruiter's search page returns Cloudflare's "Just a moment..." managed challenge. Routing that same request through Apify's UNBLOCKER proxy clears the challenge — but the page that comes back is a bare application shell with no listings anywhere in the raw HTML, because ZipRecruiter renders its results client-side after the page loads. This actor drives an actual headless browser through UNBLOCKER and waits for that rendering to finish before reading the page, rather than assuming a plain HTTP fetch is enough.
- Reads structured data first, brittle markup second. Where the rendered page carries
structured job data (a Next.js data blob, or schema.org
JobPostingmarkup), this actor reads that directly instead of depending on CSS class names that change with every frontend redesign. - Never billed for a search that finds nothing. A search with no postings is a free
found: falserow, same as every other actor in this catalogue. - Honest about scope. This is a v1: it reads the first rendered results page per search (no "load more" pagination yet), and it bills once per search, not once per posting — see "Price" for why, and the FAQ for what that means for you.
What you get
One row per search. Every row's data object carries:
| Field | Type / format | Description |
|---|---|---|
query | text | The search line you passed in, unchanged. |
found | boolean | true if the search returned at least one posting. false rows are never charged. |
data.searchKeywords | text | The keywords actually sent to ZipRecruiter. |
data.searchLocation | text | The location actually sent to ZipRecruiter. |
data.jobCount | number | How many postings this search returned after your filters. |
data.jobs | array | Every posting found, each with jobId, title, company, location, remote, salaryText, employmentType, postedAt, url, and description (truncated to 500 characters). |
scrapedAt | date (ISO) | When this actor fetched the row. |
A search that returns no postings comes back as a single found: false row and is never charged.
Price
Pay-per-event, billed once per search that finds at least one posting — never per run, and
never for a search that finds nothing. See .actor/pay_per_event.json (once Console monetization
is configured) for the exact live price.
Because this target needs a real browser session through a proxy for every search, pricing here
is per search rather than per posting — unlike this catalogue's plain-HTTP job-search actors
(seek-, xing-, bayt-jobs-lookup), which bill per posting because they don't carry that
per-search browser-session cost. A typical search returning 15-20 postings works out to a similar
effective cost per posting either way.
1,000 searches through this actor, all finding results: a flat multiple of the per-search price — see the actor's live pricing page for the current number. The same searches done by hand, opening each one in a browser and copying fields: hours of manual work.
How to use
- In the Apify Console. Open the actor page and click Start — the
itemsfield is already pre-filled with a working example. Results land in the run's dataset as soon as each search is processed. - Via the API. Call it directly with a POST request — no Console needed once you have an API
token:
curl "https://api.apify.com/v2/acts/accountable_eel~ziprecruiter-jobs-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \-X POST \-H "Content-Type: application/json" \-d '{"items":["python developer @ Austin TX"]}'
- On a schedule. Save this actor as an Apify Task with the input you want, then add a Schedule (hourly, daily, weekly) so it runs on its own — no server of your own required.
Paste one search per line:
python developer @ Austin TXregistered nursesales manager @ Chicago IL
A line with no @ location uses the location you set once in defaultLocation — leave that
blank too and the search covers everywhere ZipRecruiter has listings.
Narrowing the results — applied to the postings after they arrive:
| Input | What it does |
|---|---|
maxJobsPerQuery | Most postings to keep from the first results page. Default 20. |
titleKeywords | Keep only titles containing one of these — ["engineer","analyst"]. |
excludeTitleKeywords | Drop titles containing one of these. Applied after the include list. |
remoteOnly | Keep only roles whose location or title reads as remote. |
Input
{"items": ["python developer @ Austin TX"]}
items is a list of job searches — write each as "keywords @ location", or just the keywords and
set defaultLocation once for every line that doesn't name one.
Sample output
{"query": "python developer @ Austin TX","found": true,"data": {"searchKeywords": "python developer","searchLocation": "Austin TX","jobCount": 1,"jobs": [{"jobId": "<ziprecruiter job id>","title": "<job title>","company": "<company>","location": "<location>","remote": false,"salaryText": "<pay range, when disclosed>","employmentType": "<employment type>","postedAt": "<posted date>","url": "<job link>","description": "<description, truncated>"}]},"scrapedAt": "2026-09-08T00:00:00.000Z"}
A miss comes back as a row with "found": false and is never charged.
Use it from Clay, n8n, Make, or an AI agent
This actor runs synchronously — call it directly from a script, a workflow tool, or an AI agent, no Apify Console needed once you have an API token.
curl "https://api.apify.com/v2/acts/accountable_eel~ziprecruiter-jobs-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \-X POST \-H "Content-Type: application/json" \-d '{"items":["python developer @ Austin TX"]}'
n8n. Add an HTTP Request node: Method POST, URL
https://api.apify.com/v2/acts/accountable_eel~ziprecruiter-jobs-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>,
Body Content Type JSON, JSON Body {"items":["python developer @ Austin TX"]} (swap in an
expression from an earlier node for a real value).
Clay. Add an "HTTP API" column: Method POST, URL
https://api.apify.com/v2/acts/accountable_eel~ziprecruiter-jobs-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>,
Body {"items":["{{search}}"]}, mapping the row's search into the items array.
MCP. In Claude, Cursor, or any MCP client with the Apify MCP server, ask for "ZipRecruiter Jobs Lookup" — the agent will find and run this actor.
Tips
- Set a city in
defaultLocation, or in the search line itself, whenever you can — a narrower search returns more relevant results on the one page this actor reads. - Keep "Max parallel browser sessions" low. Each search opens a full browser session through a proxy — a handful of concurrent sessions is realistic; dozens is not.
salaryTextis populated only when the posting discloses one — many US job ads don't state pay.- This v1 reads only the first results page per search. If you need deeper coverage of a single high-volume role, split it into several narrower searches (by city, or by a title keyword) rather than expecting one search to page through hundreds of results.
vs. alternatives
| What it costs | What you get | Trade-off | |
|---|---|---|---|
This actor (ziprecruiter-jobs-lookup) | A flat price per search that finds results, nothing for a genuinely empty search | Every posting on ZipRecruiter's first results page for a keyword + location search — title, company, location, salary and apply link, filterable by title keyword and remote | Reads only the first results page (no deep pagination yet); bills per search rather than per posting, since each search needs its own browser session. |
| crawlerbros/ziprecruiter-scraper-pro | Community actor, 157 users in the last 30 days, $2 per 1,000 results | Also scrapes ZipRecruiter job search results | An established incumbent with more users and per-result pricing; this actor never charges for a miss. |
| Doing it yourself | Your time + a Cloudflare-bypass proxy, a headless browser, and re-testing when ZipRecruiter's frontend changes | The same data | This actor's proxy routing, browser automation, and extraction logic is the maintenance burden it absorbs. |
Prices for third-party tools are as researched in September 2026 and are not tracked here — check the vendor before relying on the comparison.
FAQ
Why does this actor bill per search instead of per posting, unlike some of this catalogue's other job-board actors? ZipRecruiter's search results only exist after a real browser renders the page — unlike the plain-HTTP actors in this catalogue, each search here needs its own browser session through a proxy, a real per-search cost. Billing per search reflects that directly rather than hiding it in a lower posting price.
Why does this need a proxy and a browser when some job-board actors in this catalogue don't? ZipRecruiter's search page returns a Cloudflare challenge to a plain request, and even past that challenge, the results render client-side rather than arriving in the raw HTML. Both together mean a real headless browser through Apify's UNBLOCKER proxy is required here.
Does this handle rate limits or blocking? Each search retries automatically on a transient failure. Keep "Max parallel browser sessions" low if you see repeated failures.
Do I need to configure proxies myself? No. This actor is preconfigured to route through Apify's UNBLOCKER proxy group — that's not something you need to set up in the Input tab.
Is this GDPR-relevant? The data returned is business/professional information already published publicly by employers on ZipRecruiter (job title, company, location, pay range) — not personal data about private individuals.
Can I schedule this to re-check the same searches on a cadence? Yes — set up an Apify Task with a schedule and point it at this actor with a saved input. Each scheduled run is independent; a role that appeared in yesterday's run will appear again today if it's still listed.
Can an AI agent call this directly? Yes. It's registered on the Apify MCP server — an agent in Claude, Cursor, or another MCP client can find and run it by name ("ZipRecruiter Jobs Lookup"), or you can call the REST endpoint shown above from any script or workflow tool.
Related actors
- SEEK Jobs Search Lookup — the same keyword + location search shape, against Australia's largest job board, over plain HTTP.
- XING Jobs Lookup — the DACH-region equivalent, against XING's professional network.
- Company Domain Enrichment — broader firmographic and hiring-activity enrichment once you know which companies to target.