# ZipRecruiter Scraper \[Only $0.95/1k💰] | Salary | Remote Jobs (`ahmed_jasarevic/ziprecruiter-scraper`) Actor

Scrape ZipRecruiter job listings without an API: titles, companies, salary ranges, remote/hybrid flags, employment types and full descriptions. For salary benchmarking, hiring trend monitoring and job-board aggregation.

- **URL**: https://apify.com/ahmed\_jasarevic/ziprecruiter-scraper.md
- **Developed by:** [Ahmed Jasarevic](https://apify.com/ahmed_jasarevic) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.95 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## ZipRecruiter Scraper — Job Listings, Salary & Hiring Data

Scrape ZipRecruiter job listings without an API: job titles, companies, locations, salary ranges, remote and hybrid flags, employment type, apply URLs, and full job descriptions — for salary benchmarking, hiring trend monitoring, recruitment lead generation, and job board aggregation.

### Main Use Cases

- **Salary benchmarking data** — min/max/period salary fields parsed from typed Schema.org `baseSalary`, ready for compensation analysis
- **Hiring trends monitoring** — track job posting volume by company, location, and role over time
- **Remote jobs data** — filter remote-eligible and hybrid positions with structured `isRemote` / `isHybrid` flags
- **Recruitment lead generation** — identify hiring companies and their open roles for staffing agencies and sales teams
- **Job market analysis** — analyze employment types (full time, contract, temp, internship), industries, and posting dates
- **Job board aggregator data** — keep a fresh ZipRecruiter feed for HR-tech platforms without maintaining brittle scrapers

### What Data Does the ZipRecruiter Scraper Extract?

One structured row per job, from the page's embedded JSON-LD and Next.js hydrate blobs — not fragile CSS scraping. Fields include `title`, `company`, `companyUrl`, `companyLogo`, `location`, `city`, `state`, `salary`, `salaryMin`, `salaryMax`, `salaryCurrency`, `salaryPeriod`, `isRemote`, `isHybrid`, `employmentType`, `applyUrl`, `url`, `jid`, `description`, `descriptionHtml`, `datePosted`, `validThrough`, `industry`, `searchQuery`, `searchLocation`, `scrapedAt`.

The data lives in server-rendered JSON (`JobPosting` JSON-LD + `hydrateJobCardsResponse`), which is why the actor reads it with a fast HTTP-only crawler — no headless browser, no JavaScript rendering.

### How It Works — Fast HTTP Crawling With Browser-Grade Anti-Bot

ZipRecruiter guards its JSON with Cloudflare bot management. Datacenter IPs and default TLS stacks (Node's native TLS, `got-scraping`) get a `cf-mitigated: challenge` block. The actor solves this at the network layer, not the rendering layer:

1. A custom `wreq-js` HTTP client whose Rust/BoringSSL stack **impersonates a real desktop Safari on macOS** at the TLS/HTTP-2 fingerprint level (JA3/JA4) — the only fingerprint matrix that clears the Cloudflare challenge.
2. Search and job pages are fetched through the **Apify proxy on US residential IPs** — ZipRecruiter blocks datacenter egress.
3. Search pages are parsed for the `ItemList` of `JobPosting` nodes plus the richer `hydrateJobCardsResponse` JSON blob; job detail pages add `getJobDetailsResponse` with the **full description**.
4. A `403 Just a moment...` challenge body is detected and retried with session rotation.

Because every extraction step is structured JSON parsing with DOM fallbacks, runs are 5–10× cheaper and faster than a Playwright scraper — no browser compute units, and 512 MB concurrency is plenty.

### Benchmark Salaries By Role, Location, And Pay Period

Salary fields come from typed Schema.org `baseSalary` when published — with display-string fallback. Each row carries `salaryMin`, `salaryMax`, `salaryCurrency`, and `salaryPeriod` (year/hour/etc.) so you can normalize hourly to annual and compute median vs average across jobs. ZipRecruiter postings list employer-published salary ranges more often than most boards — keep **Fetch full descriptions** ON to capture the detail-page `baseSalary` ranges for benchmarking.

### Monitor Hiring Trends And Remote Job Market Data

Set **Keywords** + **Location** (or paste a search URL from ziprecruiter.com to preserve exact filters) and run on a schedule. Use `daysPosted` to window recent postings, `remoteOnly` for remote-eligible positions, and `jobType` for employment-type filters. Track `isRemote` / `isHybrid` over time to measure the remote share by role and city.

### Input

| Field | Type | Required | Default | Notes |
|-------|------|----------|---------|-------|
| `search` | string | No | `software engineer` | Job keywords. Ignored when `startUrls` are set. |
| `location` | string | No | `Austin, TX` | City/state/ZIP, `Remote`, or `USA`. Ignored when `startUrls` are set. |
| `startUrls` | string\[] | No | `[]` | Direct ZipRecruiter search or job URLs; URL filters preserved. |
| `jobType` | string | No | `any` | `any`, `full_time`, `part_time`, `contract`, `temporary`, `internship` |
| `daysPosted` | integer | No | `0` | Only jobs posted within N days (0 = any, max 30) |
| `radiusMiles` | integer | No | `0` | Search radius (0 = default, max 100) |
| `remoteOnly` | boolean | No | `false` | Restrict to remote-eligible positions |
| `maxItems` | integer | No | `200` | Max jobs (1–500). Free users capped at 10. |
| `pagesPerSearch` | integer | No | `10` | Search-result pages per query (~20 jobs each) |
| `includeJobDetails` | boolean | No | `true` | Fetch each detail page for full description, salary, logo, apply URL |
| `maxRequestsPerCrawl` | integer | No | `1000` | Hard request cap (loop safety) |
| `maxConcurrency` | integer | No | `5` | Parallel requests (Cheerio is lightweight) |
| `proxyConfiguration` | object | No | US residential ON | Keep on US residential — datacenter IPs are Cloudflare-blocked. |

### Output

One row per job. Key fields:

| Field | Type | Description |
|-------|------|-------------|
| `title` | string | Job title |
| `company` / `companyUrl` / `companyLogo` | string | Hiring org name, URL, logo |
| `location` / `city` / `state` | string | Display location + parsed city/state |
| `salary` / `salaryMin` / `salaryMax` / `salaryCurrency` / `salaryPeriod` | string/number | Display string + parsed range |
| `isRemote` / `isHybrid` | boolean | Remote/hybrid flags from `jobLocationType`/location text |
| `employmentType` | string | Normalized (`Full Time`, `Contract`, ...) |
| `industry` | string | Company industry (detail pages) |
| `url` / `applyUrl` / `jid` | string | Job page, apply link, ZipRecruiter job id |
| `description` / `descriptionHtml` | string | Full plain-text + raw HTML |
| `datePosted` / `validThrough` | string | Posting and expiry dates from JSON-LD |
| `searchQuery` / `searchLocation` / `scrapedAt` | string | Provenance |

### Example Input

```json
{
  "search": "nurse",
  "location": "Chicago, IL",
  "maxItems": 100,
  "jobType": "full_time",
  "daysPosted": 7,
  "includeJobDetails": true
}
```

### Example Output

```json
{
  "url": "https://www.ziprecruiter.com/c/Acme/Job/Software-Engineer/-in-Austin,TX?jid=abc123",
  "title": "Software Engineer",
  "company": "Acme",
  "companyUrl": "https://www.ziprecruiter.com/c/Acme",
  "companyLogo": "https://logo.example/acme.png",
  "location": "Austin, TX",
  "city": "Austin",
  "state": "TX",
  "salary": "$80,000 - $120,000/year",
  "salaryMin": 80000,
  "salaryMax": 120000,
  "salaryCurrency": "USD",
  "salaryPeriod": "year",
  "isRemote": false,
  "isHybrid": false,
  "employmentType": "Full Time",
  "applyUrl": "https://www.ziprecruiter.com/c/Acme/Job/Software-Engineer/-in-Austin,TX?jid=abc123",
  "description": "Build things. $80K - $120K a year, full-time...",
  "datePosted": "2026-09-01",
  "jid": "abc123",
  "searchQuery": "software engineer",
  "searchLocation": "Austin, TX",
  "scrapedAt": "2026-09-05T12:00:00.000Z"
}
```

### Cost of Scraping ZipRecruiter

Pay-per-event: **$0.005 per actor start + $0.00095 per result row** (≈ $0.95 per 1 000 jobs). Because the actor is HTTP-only, a 10-job smoke run finishes in seconds and a 500-job run is well under a minute; the main variable cost is residential proxy traffic (billed per GB, and job pages are light HTML). Turn off **Fetch full descriptions** for large search-card-only sweeps, and keep **Pages per search** tight while testing.

### Integrations & Automation

Every run writes to an Apify dataset:

- Download as **JSON, CSV, Excel, XML**, or **HTML**
- Push to **Google Sheets**, **Airtable**, **PostgreSQL**, **Snowflake**, or **BigQuery** via Apify integrations
- Trigger via **Zapier**, **Make**, or Apify **webhooks**
- Call from the Apify **REST API** (any language) or **Python/JS SDKs**

Because job listings change constantly, schedule **daily runs** for hiring-trend monitoring and salary-benchmark refreshes — recurring usage also improves the actor's Apify Store ranking.

### Related Actors

- [LinkedIn Jobs Scraper](https://apify.com/curious_coder/linkedin-jobs-scraper) — the highest-traffic job scraper on Apify (⭐ 4.59, 16k+/mo)
- [Indeed Jobs Scraper](https://apify.com/valig/indeed-jobs-scraper) — ZipRecruiter's #1 US competitor (⭐ 4.73)
- [Multi Job Board Scraper](https://apify.com/openclawai/job-board-scraper) — LinkedIn, Indeed, Glassdoor, Google Jobs & ZipRecruiter in one (⭐ 5.00)
- [Indeed Scraper by Apify](https://apify.com/misceres/indeed-scraper) — sponsored/saved jobs, location filters (30k+ users)
- [Glassdoor Reviews & Jobs](https://apify.com/memo23/glassdoor-scraper-ppr) — company culture + salary intel to complement job listings (⭐ 4.57)

Together these cover the job-board data cluster: ZipRecruiter, Indeed, LinkedIn, Glassdoor, and multi-source aggregators.

### FAQ

#### Does ZipRecruiter have a public API, or how do I get ZipRecruiter job data?

No public API. ZipRecruiter's `api.ziprecruiter.com/partner/v0` is a closed partner program requiring an API key and contract. This actor is the practical alternative: it reads the structured data embedded in publicly visible pages, no key required.

#### What job listings data can I extract from ZipRecruiter?

Title, company (name, URL, logo), city/state, salary min/max/period/currency, remote and hybrid flags, employment type, industry, apply URL, job ID, full description (plain + HTML), post and expiry dates, and provenance fields.

#### How do I scrape ZipRecruiter job listings by keyword and location?

Set **Keywords** and **Location** (e.g. `nurse` + `Chicago, IL`), choose filters (job type, days posted, radius, remote-only), and set **Max jobs**. Or paste a ZipRecruiter search URL into **Start URLs** — filters in the URL are preserved and paginated with `&page=N`.

#### Is scraping ZipRecruiter, or job boards generally, legal?

The actor reads publicly listed job postings and their embedded structured data at polite concurrency, with request caps. Users are responsible for complying with ZipRecruiter's Terms of Service and `robots.txt` and applicable law — job postings are publicly displayed, and only public (non-login) data is accessed. No private applications or candidate data are collected.

#### Why is ZipRecruiter hard to scrape, and how do I avoid getting blocked?

Cloudflare bot management blocks datacenter IPs (HTTP 403) and non-browser TLS fingerprints. The actor impersonates a real desktop Safari fingerprint via a custom HTTP client and routes through the US residential proxy — with challenge detection and session rotation for retries.

#### How do I benchmark salaries by location and role from job data?

Use the parsed `salaryMin`/`salaryMax`/`salaryPeriod` fields (keep **Fetch full descriptions** ON for detail-page `baseSalary` ranges). Normalize hourly to annual, group by `location`/`city` and `title`, and compute medians. Pay-transparency states (CA, NY, CO, WA) publish salary ranges on most postings.

#### Do ZipRecruiter salary figures represent real employer pay, or estimates?

The actor extracts the employer-published `baseSalary` when present; some listings show AI-estimated ranges (marked by ZipRecruiter) or omit salary entirely — those fields are simply absent from the row. ZipRecruiter postings carry employer-published ranges more often than most boards.

#### How do I track job posting trends and hiring trends by company over time?

Run the actor on a schedule (daily/weekly) with the same queries, and merge snapshots on `jid` to measure posting velocity, re-posts, and company growth by location and role.

#### What can remote job market data tell me about remote vs hybrid trends?

`isRemote` / `isHybrid` flags let you measure the remote share by role, city, and date — useful for remote-job market intelligence and employer strategy research.

#### ZipRecruiter vs Indeed — which is better for job data?

ZipRecruiter has higher salary fill-rate (~65% vs ~40% on Indeed) and strong hourly/retail/SMB coverage; Indeed has larger raw volume and better tech-sector coverage — with 25–40% listing overlap. Use this actor for ZipRecruiter and an Indeed scraper for the other source.

#### What's the best way to build a job board aggregator from multiple sources?

Normalize each source into one schema (title, company, location, salary, type, remote flag, apply URL, posted date), dedupe on a canonical job key (e.g. company+title+location+date), and merge daily snapshots. The [Multi Job Board Scraper](https://apify.com/openclawai/job-board-scraper) covers LinkedIn, Indeed, Glassdoor, Google Jobs & ZipRecruiter.

### For AI Agents & LLM Apps

**Purpose:** Returns structured US job listings from ZipRecruiter (title, company, salary range, remote/hybrid flags, full description) as JSON rows — search by keyword + location or from direct URLs.

**Minimal input:**

```json
{ "search": "software engineer", "location": "Austin, TX", "maxItems": 50 }
```

**Variant inputs:**

```json
{ "search": "nurse", "location": "Remote", "remoteOnly": true, "daysPosted": 7, "includeJobDetails": true }
```

```json
{ "startUrls": [ { "url": "https://www.ziprecruiter.com/jobs-search?search=data+analyst&location=New+York%2C+NY" } ], "maxItems": 100 }
```

**Output fields (subset notable):** `title`, `company`, `companyUrl`, `companyLogo`, `location`, `city`, `state`, `salary`, `salaryMin`, `salaryMax`, `salaryCurrency`, `salaryPeriod`, `isRemote`, `isHybrid`, `employmentType`, `industry`, `applyUrl`, `url`, `jid`, `description`, `descriptionHtml`, `datePosted`, `validThrough`, `searchQuery`, `searchLocation`, `scrapedAt`.

**Behaviors an agent should know:**

- `search`/`location` are ignored when `startUrls` are provided — URL filters take precedence.
- `includeJobDetails: true` visits each detail page (slower, adds `description`, `salaryMin/Max`, `companyLogo`, `industry`); `false` returns faster search-card rows without salary ranges from detail JSON-LD.
- Free users are capped at 10 items regardless of `maxItems`; paying users get up to 500.
- Salary fields (`salaryMin`, `salaryMax`, `salaryPeriod`, `salaryCurrency`) are absent when the employer didn't publish a range — don't assume they're always present.
- Proxy must stay on US residential (`proxyConfiguration.apifyProxyGroups: ["RESIDENTIAL"]`, `apifyProxyCountry: "US"`) — datacenter egress gets Cloudflare-blocked.
- Billing: $0.005 per start + $0.00095 per result row ≈ $0.05 for a 50-item run (plus residential proxy GB traffic).

### Legal & Compliance Disclaimer

This actor is an independent tool and is not affiliated with, endorsed by, or sponsored by ZipRecruiter, Inc. It accesses only publicly visible job postings and their embedded structured data on ziprecruiter.com — no login bypass, no CAPTCHA solving, no private or candidate data. Users are responsible for complying with ZipRecruiter's Terms of Service, `robots.txt`, and applicable law.

### SEO Keywords

ziprecruiter scraper, ziprecruiter jobs scraper, ziprecruiter data api, ziprecruiter job listings data, salary benchmarking data, job listings scraper, job market analysis data, remote jobs data, recruitment lead generation, compensation benchmarking, hiring trends monitoring, job board aggregator data, ziprecruiter alternative, hr tech data, talent acquisition data, job posting trends, company hiring signals, salary data scraper, linkedin jobs scraper, indeed scraper, glassdoor scraper, monster jobs scraper

# Actor input Schema

## `search` (type: `string`):

Job keywords, e.g. 'software engineer', 'nurse', 'truck driver'. Ignored when Start URLs are provided.

## `location` (type: `string`):

City/state/ZIP or 'Remote' or 'USA'. Ignored when Start URLs are provided.

## `startUrls` (type: `array`):

Optional ZipRecruiter search-result or job-detail URLs. Any filters in the URL are preserved. When empty, the actor builds a search URL from Keywords + Location.

## `jobType` (type: `string`):

Employment-type filter applied when building the search URL.

## `daysPosted` (type: `integer`):

Only jobs posted within the last N days (0 = any, max 30).

## `radiusMiles` (type: `integer`):

Search radius in miles (0 = ZipRecruiter default, max 100).

## `remoteOnly` (type: `boolean`):

Restrict results to remote-eligible positions.

## `maxItems` (type: `integer`):

Maximum job records to return (1-500). Free users are capped at 10.

## `pagesPerSearch` (type: `integer`):

How many search-result pages to walk per query (each page holds ~20 jobs).

## `includeJobDetails` (type: `boolean`):

Visit each job detail page and parse its JobPosting JSON-LD for the full description, salary, company logo and apply URL. Turn off for faster search-card-only runs.

## `maxRequestsPerCrawl` (type: `integer`):

Hard cap on total HTTP requests (loop safety).

## `maxConcurrency` (type: `integer`):

Parallel HTTP requests. Cheerio is lightweight so you can go higher than with a browser. Keep moderate to stay polite.

## `proxyConfiguration` (type: `object`):

ZipRecruiter blocks datacenter IPs with Cloudflare. Keep the US residential Apify proxy ON. This Actor uses fast HTTP-only (Cheerio) fetching through a residential pool.

## Actor input object example

```json
{
  "search": "software engineer",
  "location": "Austin, TX",
  "startUrls": [],
  "jobType": "any",
  "daysPosted": 0,
  "radiusMiles": 0,
  "remoteOnly": false,
  "maxItems": 200,
  "pagesPerSearch": 10,
  "includeJobDetails": true,
  "maxRequestsPerCrawl": 1000,
  "maxConcurrency": 5,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "search": "software engineer",
    "location": "Austin, TX"
};

// Run the Actor and wait for it to finish
const run = await client.actor("ahmed_jasarevic/ziprecruiter-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "search": "software engineer",
    "location": "Austin, TX",
}

# Run the Actor and wait for it to finish
run = client.actor("ahmed_jasarevic/ziprecruiter-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "search": "software engineer",
  "location": "Austin, TX"
}' |
apify call ahmed_jasarevic/ziprecruiter-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,ahmed_jasarevic/ziprecruiter-scraper"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/XO6A4d8fHn9q54heD/builds/cGZdJDEfeSY9mDgBE/openapi.json
