Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Short answer: do not send an unattended crawler to ZipRecruiter unless you have explicit permission. ZipRecruiter’s current Terms of Use prohibit automated bots, scrapers and spiders, excessive automated requests, collecting personal information and bypassing access controls. For an approved integration, use the authenticated Partner Platform Jobs API and define your own validated JSON contract. Only parse HTML when the page owner has authorized that use.

This guide shows the API-first approach, an authorization-limited HTML parser, deterministic field mapping, validation, webhook delivery and recovery steps. It also explains how to capture an authorized listing page when a screenshot is useful without confusing visual capture with structured job data.

Is ZipRecruiter scraping allowed?

Assume that direct automated scraping is not allowed unless ZipRecruiter has granted permission for your specific use. Its current Terms of Use prohibit crawling or scraping with automated bots, scrapers or spiders; automated access that sends more requests than a human could reasonably generate; collection of personal information; and bypassing access controls. A login wall, CAPTCHA, rate limit or other technical restriction is not an invitation to find a workaround.

Before writing code, document your authorization, the fields you are allowed to collect, retention limits, request budget and deletion process. Follow privacy, employment, intellectual-property and data-access laws that apply to your organization and users. ZipRecruiter’s Job Posting Rules also prohibit non-employment arrangements, multi-level marketing, unpaid internships, personal information in job descriptions or application instructions, irrelevant keywords and product or service promotion in job postings.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
#1 Best Overall
Sale
Pearson Computer Networking, 8E
  • brand: Pearson
  • Computer Networking, 8e

How to get ZipRecruiter jobs without scraping

Use the Partner Platform Jobs API when you are eligible

ZipRecruiter documents a Partner Platform Jobs API for authorized partners. It represents a job as JSON and supports creating, updating, retrieving and closing listings. The documented resource is https://api.ziprecruiter.com/partner/v0/job. Authentication uses HTTP Basic authentication with an API key. Confirm your partner account’s permitted operations and quotas before moving this into production.

The API route is preferable because the schema, authentication and lifecycle are explicit. You can preserve a stable identifier, validate required fields and retain a source URL without reverse-engineering changing markup.

Receive applications through the Apply Webhook

If your integration handles applications, ZipRecruiter’s Apply Webhook requires a Jobs API integration and an HTTPS endpoint that accepts JSON POST requests. Deliver those payloads into your ATS or internal service rather than scraping an application page. Verify signatures or other authentication details that ZipRecruiter provides for your account before accepting production traffic.

Choose an output contract before fetching data

A “clean” result is a contract, not merely minified JSON. Keep the source identifier and URL so every record can be traced, map optional values deliberately and reject records that fail required-field checks.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
{
  "job_id": "string",
  "title": "string",
  "employer": "string",
  "location": {
    "city": "string",
    "state": "string",
    "country": "string"
  },
  "employment_type": "string|null",
  "description": "string",
  "url": "string",
  "source": "ziprecruiter",
  "retrieved_at": "ISO-8601 timestamp"
}

This normalized shape is an implementation contract. ZipRecruiter’s documented model uses names such as job_id, title, job_type, city, state, country, employer_id, employer_name, description and preview_url; it does not claim to emit the normalized names above verbatim.

Field mapping and null policy

Normalized field API source Rule
job_id job_id Required; preserve as a string and use as the upsert key.
title title Required; trim surrounding whitespace.
employer employer_name Required for publication; do not substitute an unverified value.
location city, state, country Keep each component; use null for an absent optional component.
employment_type job_type Use null when not supplied; do not infer from title text.
description description Preserve text as text; remove markup only in your own normalization layer.
url preview_url Retain the original URL for traceability.
source constant Set to ziprecruiter.
retrieved_at your system clock Write an ISO-8601 UTC timestamp at receipt time.

Python: request and normalize an authorized API response

The following program is a runnable normalization layer. Set the endpoint and credentials supplied for your authorized partner account. Because account-specific operation paths and response envelopes can differ, inspect the documented response and adjust the extract_jobs function rather than guessing at fields.

import os
from datetime import datetime, timezone
import requests

API_URL = "https://api.ziprecruiter.com/partner/v0/job"
API_KEY = os.environ["ZIPRECRUITER_API_KEY"]


def first(value):
    return value.strip() if isinstance(value, str) and value.strip() else None


def normalize(job):
    required = ("job_id", "title", "employer_name", "description", "preview_url")
    missing = [name for name in required if not first(job.get(name))]
    if missing:
        raise ValueError(f"missing required fields: {', '.join(missing)}")

    return {
        "job_id": str(job["job_id"]),
        "title": first(job["title"]),
        "employer": first(job.get("employer_name")),
        "location": {
            "city": first(job.get("city")),
            "state": first(job.get("state")),
            "country": first(job.get("country")),
        },
        "employment_type": first(job.get("job_type")),
        "description": first(job.get("description")),
        "url": first(job.get("preview_url")),
        "source": "ziprecruiter",
        "retrieved_at": datetime.now(timezone.utc).isoformat(),
    }


def extract_jobs(payload):
    # Accept either one job object or a documented list envelope.
    if isinstance(payload, dict) and isinstance(payload.get("jobs"), list):
        return payload["jobs"]
    if isinstance(payload, dict):
        return [payload]
    raise ValueError("unexpected JSON response shape")


response = requests.get(
    API_URL,
    auth=(API_KEY, ""),
    timeout=30,
)
response.raise_for_status()
normalized = [normalize(job) for job in extract_jobs(response.json())]
print(__import__("json").dumps(normalized, ensure_ascii=False, indent=2))

Run it with ZIPRECRUITER_API_KEY set in the environment. Store the key in a secret manager in production; never commit it, place it in a URL, or print it in logs. If your partner documentation specifies a different HTTP method or an operation-specific path, retain the same normalization and validation functions while using that documented request.

cURL and Node.js equivalents

cURL

curl --fail --silent --show-error 
  --user "$ZIPRECRUITER_API_KEY:" 
  --header "Accept: application/json" 
  "https://api.ziprecruiter.com/partner/v0/job"

This retrieves the endpoint with Basic authentication. Use the create, update or close method and body documented for your partner account; do not invent fields or send a listing until required validation and posting-rule checks pass.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Node.js (18 or newer)

const apiKey = process.env.ZIPRECRUITER_API_KEY;
if (!apiKey) throw new Error('ZIPRECRUITER_API_KEY is required');

const token = Buffer.from(`${apiKey}:`).toString('base64');
const res = await fetch('https://api.ziprecruiter.com/partner/v0/job', {
  headers: {
    'Authorization': `Basic ${token}`,
    'Accept': 'application/json'
  },
  signal: AbortSignal.timeout(30000)
});

if (!res.ok) throw new Error(`ZipRecruiter returned ${res.status}`);
const payload = await res.json();
console.log(JSON.stringify(payload, null, 2));

Parsing HTML only with explicit authorization

If you have written permission to parse a particular page, keep the collector narrow and polite. Use a normal request budget, identify and honor access restrictions, collect the minimum fields needed, and stop on a CAPTCHA, login wall, rate limit or blocked response. Do not collect resumes or other personal information, and never defeat a technical control.

import json
from datetime import datetime, timezone
import requests
from bs4 import BeautifulSoup

URL = "AUTHORIZED_LISTING_URL"
r = requests.get(
    URL,
    headers={"User-Agent": "AuthorizedJobIndexer/1.0 (contact: ops@example.com)"},
    timeout=30,
)
r.raise_for_status()
if "captcha" in r.text.lower() or "access denied" in r.text.lower():
    raise RuntimeError("stop: access control encountered")

soup = BeautifulSoup(r.text, "html.parser")
def text(selector):
    node = soup.select_one(selector)
    return node.get_text(" ", strip=True) if node else None

record = {
    "job_id": text("[data-job-id]"),
    "title": text("h1"),
    "employer": text("[data-employer]"),
    "location": {"city": None, "state": None, "country": None},
    "employment_type": None,
    "description": text("[data-description]"),
    "url": URL,
    "source": "ziprecruiter",
    "retrieved_at": datetime.now(timezone.utc).isoformat(),
}
if not record["job_id"] or not record["title"]:
    raise ValueError("required selector did not produce a value")
print(json.dumps(record, ensure_ascii=False, indent=2))

The selectors in this example are placeholders for selectors your authorized page owner documents. Do not assume that a public-looking class name grants permission or that a selector will remain stable. Version your parser, retain the raw URL and timestamp, and send schema failures to a review queue instead of silently emitting partial records.

API versus authorized HTML parsing

Concern Partner API Authorized HTML parser
Authorization Restricted to eligible partners with an API key. Requires explicit permission for the pages and fields collected.
Schema stability Documented JSON model and lifecycle operations. Selectors can change when markup changes.
Authentication HTTP Basic authentication with an API key. Use only credentials and headers expressly authorized.
Rate and volume limits Follow the quotas and limits on your partner agreement; public performance figures are not stated here. Use a conservative, agreed request budget; do not imitate high-volume browsing.
Maintenance Update against API version changes. Maintain selectors, access checks and parser tests.
Data minimization Request and store only permitted fields. Filter personal information before persistence.
Traceability Store job_id and preview_url. Store source URL, retrieval time and parser version.
Cost Commercial terms are not stated in the available documentation. Infrastructure cost depends on your system; no benchmark is established here.

Validation, deduplication and storage

Validate before writing

  • Require a non-empty stable job_id, title, employer, description and source URL.
  • Normalize whitespace and Unicode without changing the meaning of the description.
  • Represent missing optional values as null, not an invented string such as “N/A”.
  • Reject malformed URLs and timestamps before the record reaches downstream consumers.
  • Apply a maximum description size appropriate to your database and retain an error reason for rejected records.

Upsert by the source identifier

Use job_id as the natural key. An upsert prevents duplicate rows when a listing is retrieved repeatedly. Keep retrieved_at for auditability and, if your agreement allows it, retain a change history so updates and closures can be explained. Do not treat a missing job in one response as proof that it has closed unless the API operation explicitly reports that state.

Protect personal and sensitive data

Job descriptions and application payloads can contain personal information. Apply least-privilege access, encryption, retention and deletion controls. Do not copy resumes or applicant details into a general search index merely because an HTML page exposes them.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Posting and downstream compliance checks

ZipRecruiter’s Job Posting Rules place responsibility for employment, privacy, data-access, intellectual-property and other applicable laws on the poster. Add a pre-publication checklist:

  • Confirm the role is a paid employment opportunity and not a prohibited arrangement.
  • Remove personal information from the description and application instructions.
  • Remove irrelevant keywords and promotional product or service copy.
  • Verify that the employer, location and employment type are accurate.
  • Log who authorized the post and which API operation created or changed it.

Troubleshooting common failures

401 or 403 response

Check that the API key belongs to an eligible partner, that Basic authentication encodes the key as the username with an empty password, and that the requested operation is enabled. Do not retry indefinitely and do not attempt to bypass authorization.

404 response

Confirm you are using the documented versioned endpoint and operation path for your account. A listing identifier may be required for retrieve, update or close operations; use the identifier returned by the API rather than a scraped URL.

429 or repeated throttling

Stop sending requests, honor any Retry-After value, apply exponential backoff with jitter and reduce concurrency. If no limit is documented for your account, ask your partner contact instead of estimating a safe ceiling.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

200 response but no jobs

Log the response content type and validate its envelope. The API may return one object while your code expects a jobs array. Keep the raw response in a restricted diagnostic store and adjust extraction only to documented fields.

HTML parser returns null fields

The page may have changed, rendered content client-side or served an authorization interstitial. Treat missing required selectors as a failed record, not as an invitation to scrape another endpoint. Update selectors only under the original permission.

Invalid JSON downstream

Serialize with your language’s JSON encoder, use UTF-8, escape control characters and validate against a schema before publishing. Never concatenate strings to build JSON.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Performance and reliability practices

  • Set explicit connect and read timeouts and cap retries to idempotent reads.
  • Use an idempotency strategy for create operations if your partner API documents one; otherwise record the request and returned identifier so a retry cannot create an accidental duplicate.
  • Queue work and limit concurrency to the agreement’s request budget.
  • Emit structured logs containing operation, status, latency, record identifier and a redacted error; never log API keys or applicant data.
  • Test normalization with fixtures for missing location components, Unicode titles, long descriptions and duplicate identifiers.
  • Monitor validation-failure rate and authorization errors separately so a markup change is not mistaken for an access problem.

Or skip the browser setup

ScreenshotNeo is a visual capture API, not a replacement for ZipRecruiter’s Jobs API and not a way around access controls. For an authorized listing page, one GET request can produce a PNG, JPEG, WebP or PDF without installing a browser:

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://www.ziprecruiter.com/ -o shot.webp

See the ScreenshotNeo documentation for options. Before capture it can accept the cookie or consent banner like a visitor and remove more than 60 known consent platforms, newsletter popups and chat widgets; each cleanup step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and the response identifies the page verdict and billing status in X-Page-Verdict and X-Billed headers. Its MCP server exposes take_screenshot, get_page_info and capture_pdf to Claude, Cursor and other MCP clients. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots, with every feature on every plan.

Use screenshots for visual review or audit evidence, while the authorized API remains the source for machine-readable jobs. Create a free ScreenshotNeo account to get 1,000 screenshots a month without a card.

Frequently Asked Questions

Can I use a public ZipRecruiter URL as permission to scrape it?

No. Public visibility does not override ZipRecruiter’s Terms of Use. Obtain explicit authorization or use an eligible Partner Platform integration.

Does the Partner Platform Jobs API return the normalized JSON shown here?

Not necessarily. The normalized shape is an application-level contract; map documented fields such as job_id, employer_name and preview_url into it and validate the result.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Should I scrape application pages to collect applicants?

No. When enabled for your integration, use the Apply Webhook over HTTPS and apply strict privacy and retention controls.

Can ScreenshotNeo turn ZipRecruiter pages into job JSON?

No. ScreenshotNeo captures authorized pages as images or PDFs. Use the Jobs API or an explicitly authorized parser for structured records.