Vexaloom Labs

Use ZipRecruiter Job Search Results & Salary Scraper with Python

Extract job postings from ZipRecruiter.com search results - title, company, location, parsed salary range, city, state, and apply URL. Search by keyword and location or supply your own filtered search URL.

1. Choose the data you need

Review the published README for supported collection modes, examples, and limits. The recipe below uses the current published input definition.

2. Prepare a source-based input

This JSON contains published prefills and required-field defaults. Review every value for your task before running it; it is not a promise of successful collection.

{
  "daysPosted": 0,
  "experienceLevels": [],
  "fetchDescriptions": false,
  "includeCompanyProfile": false,
  "jobType": "any",
  "locations": [
    "Chicago, IL"
  ],
  "maxItems": 3,
  "maxSearches": 1,
  "mode": "search",
  "radiusMiles": 0,
  "salaryLocations": [
    "Chicago, IL"
  ],
  "salaryMax": 0,
  "salaryMin": 0,
  "salaryRoles": [
    "registered nurse"
  ],
  "salaryUrls": [],
  "searchQueries": [
    "registered nurse"
  ],
  "startUrls": [],
  "workplace": "any"
}

Download input.example.json and save your reviewed copy as input.json.

FieldTypeRequiredDescription
modestringoptionalSearch job postings, scrape URLs you already have, or get salary benchmarks for a role and location.
searchQueriesarrayoptionalJob titles or keywords, e.g. `registered nurse` or `software engineer`. Every keyword is searched in every location below.
locationsarrayoptionalCities, states, ZIP codes or `United States` for a nationwide search. Every keyword is searched in every location.
companyNamestringoptionalOnly jobs from this employer, e.g. `Visa`. Leave the keywords empty to browse every open role at the company.
startUrlsarrayoptionalSearch, job, company or salary URLs. Use this with `Scrape specific URLs`; filters already in a search URL are kept.
jobTypestringoptionalEmployment-type filter for keyword searches.
workplacestringoptionalWork arrangement for keyword searches.
experienceLevelsarrayoptionalRestrict to one or more seniority levels. Empty means no filter.
daysPostedintegeroptionalOnly jobs posted within the last N days. 0 means any time.
radiusMilesintegeroptionalDistance around each location. 0 uses ZipRecruiter's default.
salaryMinintegeroptionalOnly jobs whose advertised annual pay reaches this amount. 0 means no lower bound.
salaryMaxintegeroptionalOnly jobs whose advertised annual pay stays at or below this amount. Best effort; 0 means no upper bound.
fetchDescriptionsbooleanoptionalFetch each posting's detail page for the complete description, benefits, company profile and apply details. Off gives faster card-only rows.
includeCompanyProfilebooleanoptionalAdd industry, website, size, headquarters, rating and about text from the posting's company data.
maxItemsintegeroptionalMaximum number of job or salary records to return across all searches and URLs.
maxSearchesintegeroptionalCap on keyword × location combinations in search mode.
salaryRolesarrayoptionalJob titles for salary benchmarks, e.g. `registered nurse` or `data analyst`. Used in salary mode.
salaryLocationsarrayoptionalCities, states or `United States` for salary benchmarks. Every role is looked up in every location.
salaryUrlsarrayoptionalExact ZipRecruiter `/Salaries/...` URLs. When set, these are used instead of role and location pairs.

3. Start an Actor run

Set APIFY_TOKEN in your environment. Python 3.10 or newer is sufficient; this recipe uses no third-party packages.

Execution may incur charges. Review current pricing on Apify. The request asks for a 120-second timeout and a charge limit; these settings are subject to Apify’s pricing and do not guarantee an all-inclusive spending cap.

import json
import os
import urllib.request

# Save the schema-derived example as input.json; review and edit it first.
with open("input.json", encoding="utf-8") as stream:
    actor_input = json.load(stream)

token = os.environ["APIFY_TOKEN"]
request = urllib.request.Request(
    "https://api.apify.com/v2/acts/vxRSmgrG9noDPw1BM/runs?timeout=120&maxTotalChargeUsd=0.50",
    data=json.dumps(actor_input).encode("utf-8"),
    headers={"Authorization": "Bearer " + token, "Content-Type": "application/json"},
    method="POST",
)
# This starts a potentially billable run. Do not automatically retry this POST.
with urllib.request.urlopen(request, timeout=40) as response:
    run = json.load(response)["data"]
print("Run ID:", run["id"])
print("Status:", run["status"])
# Inspect and download results in Apify Console once the run succeeds.

4. Inspect the actual results

Open the run in Apify Console and wait for a successful completion. Use the published output schema to locate results and the dataset schema to understand declared views. Missing or optional record fields must not be treated as guaranteed values.

One flat dataset with job postings and salary benchmark records. Job rows carry the full description, structured pay, company profile, benefits, dates and apply link; salary rows carry median, percentile and average pay.

Questions before you run

Which inputs are required?

No top-level fields are marked required in the published schema. Conditional requirements can still apply; check the README.

What does this guide verify?

The fields, prefills, and schema links are rendered from the published build. This is a usage recipe, not a paid live extraction test. Source availability and collection results can vary.

What if the run request times out?

Check Apify Console before retrying. The first request may already have created a run. Do not automatically repeat a run-creation POST.

Generated from source build MiuZfLBEFxbiXz4Uj and its README and schemas. Source changes regenerate this guide; no third-party editorial claims are inferred.