# ZipRecruiter Jobs & Salary Scraper (`rainminer/ziprecruiter-jobs-scraper`) Actor

Scrape ZipRecruiter job listings by keyword and location, or from any search URL. Get job title, company, location, salary, employment type, posting date and the full job description. Every detail page is opened, and incomplete rows are flagged instead of silently returned empty.

- **URL**: https://apify.com/rainminer/ziprecruiter-jobs-scraper.md
- **Developed by:** [rainminer](https://apify.com/rainminer) (community)
- **Categories:** Jobs, Lead generation, Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $0.37 / 1,000 job with descriptions

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#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 Jobs Scraper

![ZipRecruiter](https://www.ziprecruiter.com/favicon.ico)

The **ZipRecruiter Jobs Scraper** extracts job listings from [ZipRecruiter](https://www.ziprecruiter.com/) search result pages. Search by keyword and location or paste any ZipRecruiter search URL, then export structured job data — titles, companies, locations, salaries, posting dates, and the **complete job description** — to JSON, CSV, Excel, or via the Apify API.

Use it for job-market research, recruiting intelligence, salary benchmarking, aggregator feeds, or building a jobs dataset.

### Why this scraper?

ZipRecruiter is protected by Cloudflare, and many scrapers succeed on the search page but return **empty descriptions** when the detail page is blocked — while still charging you for those broken rows. This Actor is built around a **completeness guarantee**:

- Every job's detail page is opened to extract the **full description**.
- If a description cannot be fetched, the row is still returned but clearly flagged with `descriptionFetchFailed: true`.
- **You are only charged for complete records** (jobs that include a real description). Flagged, partial rows are never billed.

### What can the ZipRecruiter Jobs Scraper do?

- Scrape jobs from any ZipRecruiter search URL (keyword + location, filters, etc.).
- Follow pagination automatically until your `maxItems` limit is reached (per start URL).
- Extract the full job description from each listing's detail page.
- Parse salary ranges, employment type, posting date, and remote status when available.
- Flag incomplete records instead of silently returning empty descriptions.
- Run on a schedule with Apify dataset exports, API access, webhooks, and proxy configuration.

### How to use it

Enter a `search` keyword and optional `location`/filters, or paste existing
ZipRecruiter search URLs into `startUrls`. When `startUrls` is provided, it
takes precedence and all filters already present in those URLs are preserved.

### Input

| Field                  | Type     | Description                                                                |
| ---------------------- | -------- | -------------------------------------------------------------------------- |
| `startUrls`            | string\[] | ZipRecruiter search URLs; takes precedence over keyword fields.            |
| `search`               | string   | Keyword, title, skill, or company.                                         |
| `location`             | string   | City, state, or ZIP code.                                                  |
| `jobType`              | string   | `any`, `full_time`, `part_time`, `contract`, `internship`, or `temporary`. |
| `daysPosted`           | integer  | Posted within N days; `0` means any date.                                  |
| `radiusMiles`          | integer  | Search radius; `0` uses ZipRecruiter's default.                            |
| `remoteOnly`           | boolean  | Restrict keyword searches to remote-eligible jobs.                         |
| `maxItems`             | integer  | Maximum jobs to return per start URL (default `50`).                       |
| `fetchFullDescription` | boolean  | Extended option: fetch full descriptions (default `true`).                 |
| `proxyConfiguration`   | object   | US residential proxy settings (the compatible default).                    |

#### Input example

```json
{
  "search": "software engineer",
  "location": "Austin, TX",
  "jobType": "full_time",
  "daysPosted": 7,
  "radiusMiles": 25,
  "remoteOnly": false,
  "maxItems": 25,
  "fetchFullDescription": true,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": ["RESIDENTIAL"],
    "apifyProxyCountry": "US"
  }
}
```

### Output

| Field                             | Description                                                        |
| --------------------------------- | ------------------------------------------------------------------ |
| `type`                            | Pro-compatible row type: `job_ziprecruiter`                        |
| `id`                              | ZipRecruiter card-level job identifier                             |
| `jid`                             | Job ID parsed from the `jid` URL parameter                         |
| `title`                           | Job title                                                          |
| `company`                         | Hiring company name                                                |
| `companyUrl` / `companyLogo`      | Company profile and logo URLs when available                       |
| `location`                        | Job location (city, region, country)                               |
| `city` / `state`                  | Parsed city and two-letter state                                   |
| `isRemote`                        | `true` when the job is remote/telecommute, otherwise `null`        |
| `isHybrid`                        | `true` when the location is hybrid, otherwise `null`               |
| `employmentType`                  | Employment type (e.g. `FULL_TIME`) when available                  |
| `salary`                          | Human-readable salary string when available                        |
| `salaryMin` / `salaryMax`         | Parsed salary bounds when available                                |
| `salaryCurrency` / `salaryPeriod` | Salary currency and period (e.g. `USD`, `year`)                    |
| `datePosted`                      | Posting date (ISO 8601)                                            |
| `validThrough`                    | Application deadline when available                                |
| `description`                     | Full job description as plain text                                 |
| `descriptionHtml`                 | Original description HTML when available                           |
| `descriptionFetchFailed`          | `true` when the full description could not be fetched (not billed) |
| `url`                             | Canonical job detail URL                                           |
| `searchUrl`                       | The search URL this job came from                                  |
| `source`                          | Always `ziprecruiter`                                              |
| `scrapedAt`                       | Timestamp of extraction (ISO 8601)                                 |

If an entire search is blocked after all retries, the Actor emits the same
compatibility sentinel shape as the incumbent:
`{"type":"job_ziprecruiter_blocked","message":"...","requestedUrls":[...]}`.
Sentinel and incomplete rows are not billed.

#### Output example

```json
{
  "type": "job_ziprecruiter",
  "id": "TT2JIBaZ_bbThXFmHQVtWw",
  "jid": "abc123def456",
  "title": "Senior Software Engineer",
  "company": "Acme Corp",
  "companyUrl": "https://www.ziprecruiter.com/co/Acme-Corp/Jobs/-in-Austin,TX",
  "companyLogo": "https://www.ziprecruiter.com/svc/fotomat/example.png",
  "location": "Austin, TX, US",
  "city": "Austin",
  "state": "TX",
  "isRemote": null,
  "isHybrid": null,
  "employmentType": "FULL_TIME",
  "salary": "USD 120000 - 160000 / year",
  "salaryMin": 120000,
  "salaryMax": 160000,
  "salaryCurrency": "USD",
  "salaryPeriod": "year",
  "datePosted": "2026-08-01",
  "validThrough": "2026-09-01",
  "description": "We are hiring a Senior Software Engineer...",
  "descriptionFetchFailed": false,
  "url": "https://www.ziprecruiter.com/c/Acme-Corp/Job/Senior-Software-Engineer/-in-Austin,TX?jid=abc123def456",
  "searchUrl": "https://www.ziprecruiter.com/jobs-search?search=software+engineer&location=Austin%2C+TX",
  "source": "ziprecruiter",
  "scrapedAt": "2026-08-08T06:40:00.000Z"
}
```

### How much does it cost?

This Actor uses **pay-per-event pricing plus platform usage**:

| Charge                                             | Amount                                                                |
| -------------------------------------------------- | --------------------------------------------------------------------- |
| Complete job (`job-with-description`)              | **$0.49 per 1,000** on Free (down to $0.37 per 1,000 on higher plans) |
| Actor start                                        | $0.00005 per GB of memory                                             |
| Apify platform usage (compute + residential proxy) | Billed to your account at standard Apify rates                        |

Two things worth knowing before you run it:

- **You are only charged for complete records.** A job is billed when it includes
  a real job description. Rows returned with `descriptionFetchFailed: true` are
  still delivered so you can see what happened, but they are never charged.
- **You pay the platform usage.** ZipRecruiter is behind Cloudflare, so this
  Actor drives a real browser through US residential proxies. In our
  measurements that works out to roughly **$18 of Apify usage per 1,000 jobs**,
  dominated by residential proxy traffic. The per-result fee above is a small
  maintenance fee on top, not the full cost — budget accordingly, and use
  `maxItems` to keep test runs cheap.

### Notes and limitations

- ZipRecruiter uses Cloudflare bot protection, so **US residential proxies are strongly recommended** (they are the default). Runs on datacenter IPs will typically be blocked.
- Job availability changes quickly — treat scraped data as a point-in-time snapshot.
- Only public search results are scraped. This Actor does not log in to any account.

### Is it legal to scrape ZipRecruiter?

This Actor extracts publicly visible job listing information. Use it responsibly, respect ZipRecruiter's terms, and ensure your use case complies with applicable laws and regulations. If you are unsure whether your use case is allowed, consult a legal professional.

### Image Credit

Image credit: [ziprecruiter.com](https://www.ziprecruiter.com/)

# Actor input Schema

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

ZipRecruiter search-result URLs. Any filters already present in each URL are preserved. When provided, search and location are ignored.

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

Job keyword, title, skill, or company. Combined with location when startUrls is empty.

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

City, state, or ZIP code to combine with search. Ignored when startUrls is provided.

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

Employment type filter. Only applied when search is used to build the URL.

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

Only include jobs posted within the last N days. 0 means any date. Only applied when search is used.

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

Geographic radius around location. 0 uses ZipRecruiter's default. Only applied when search is used.

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

Restrict results to remote-eligible positions. Only applied when search is used.

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

Maximum number of jobs to return per start URL. Pagination stops automatically once this limit is reached.

## `fetchFullDescription` (type: `boolean`):

Open each job's detail page to extract the complete description. Turn off for a faster, list-only run (descriptions may be truncated or missing).

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

ZipRecruiter is protected by Cloudflare, so US residential proxies are required for reliable results.

## Actor input object example

```json
{
  "startUrls": [
    "https://www.ziprecruiter.com/jobs-search?search=software+engineer&location=New+York%2C+NY"
  ],
  "jobType": "any",
  "daysPosted": 0,
  "radiusMiles": 0,
  "remoteOnly": false,
  "maxItems": 3,
  "fetchFullDescription": true,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}
```

# Actor output Schema

## `overview` (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 = {
    "startUrls": [
        "https://www.ziprecruiter.com/jobs-search?search=software+engineer&location=New+York%2C+NY"
    ],
    "maxItems": 3,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "US"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("rainminer/ziprecruiter-jobs-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 = {
    "startUrls": ["https://www.ziprecruiter.com/jobs-search?search=software+engineer&location=New+York%2C+NY"],
    "maxItems": 3,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "US",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("rainminer/ziprecruiter-jobs-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 '{
  "startUrls": [
    "https://www.ziprecruiter.com/jobs-search?search=software+engineer&location=New+York%2C+NY"
  ],
  "maxItems": 3,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}' |
apify call rainminer/ziprecruiter-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,rainminer/ziprecruiter-jobs-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/cUQbAqiFFKtZcR6iI/builds/SHqNgtkBOFKIqJDPd/openapi.json
