Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Repair Windows errors before they cause bigger problemsFix Now →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.
#1 Best Overall
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.
Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Repair Windows errors before they cause bigger problems3Scan for outdated or missing drivers - takes under a minute{
"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.
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →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.
Rank #3
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.
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.
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.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.
Best Value
- Used Book in Good Condition
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.
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteShould 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.

