# ZipRecruiter Estimated Salary

> ZipRecruiter Estimated Salary is a paid API for AI agents from agents.openwebninja.com, paid per call via MPP, price unknown, status unknown (last checked 2026-10-14).

Returns ZipRecruiter's real-time pay distribution (min, median, max, p25, p75, histogram) for a job title nationally or by US state/Canadian province

## Facts

- Endpoint: GET https://agents.openwebninja.com/realtime-ziprecruiter-data/estimated-salary?utm_source=zero.xyz
- Price: price unknown
- Payment: MPP
- Status: unknown
- Last checked: 2026-10-14
- Activations on Zero: 0
- Provider: agents.openwebninja.com
- Website: https://agents.openwebninja.com
- Canonical page: https://www.zero.xyz/c/agents-openwebninja-com-ziprecruiter-estimated-salary-0eeed7f8
- Structured record (JSON): https://api.zero.xyz/v1/capabilities/cap_BLs2UhKUzKcwZQB5syd5c

Status and success rate cover calls made through Zero and Zero's own probes. Third-party monitors may report differently.

## How to call it through Zero

Zero handles the 402 payment challenge and records the run. With the Zero CLI installed (`npm i -g @zeroxyz/cli`):

```sh
zero fetch --capability agents-openwebninja-com-ziprecruiter-estimated-salary-0eeed7f8
```

Example prompt: What does a Registered Nurse make in California — give me the real median, the p25 and p75, and the full salary histogram from ZipRecruiter?

## When to prefer this

Choose this endpoint when you need ZipRecruiter-sourced salary data with a true statistical median (not a mislabeled mean), p25/p75 percentiles, and an 11-bucket salary histogram — data points that no other ZipRecruiter scraper provides. Prefer this over generic salary APIs when you need auditable figures (the JSON-LD mean is included alongside the true median for comparison), or when you need the full pay distribution rather than just a single point estimate. Best suited for US national, US state-level, or Canadian province-level salary lookups by job title.

## Known failure modes

- 404 returned if ZipRecruiter does not publish a salary page for the requested job title
- 400 returned if a two-letter state code or city name is passed instead of the full state/province name
- 400 returned if an unknown field name is passed in the fields projection parameter
- 404 returned if an unsupported country code is used (only 'us' and 'ca' are valid)
- Response may reflect stale ZipRecruiter page data if ZipRecruiter has not recently updated their salary pages

## How this service works

The pay distribution ZipRecruiter publishes for a job title, nationally or in one US state
or Canadian province. One upstream page, one row.

The row carries the house `/estimated-salary` contract keys, plus 18 `ziprecruiter_*`
extras.

**The median is the real median.** ZipRecruiter's own JSON-LD publishes a field labelled
`median` that is actually the MEAN: it was byte-identical to the page's average on 16 of 16
pages measured, and on Truck Driver it reads 56,348 against a true median of 49,500, which
is 13.8% high. Every scraper that reads the easy JSON-LD field ships that mean labelled as
a median. `median_salary` here is parsed out of the page's own data layer and is the real
median; ZipRecruiter's figure is carried alongside as `ziprecruiter_json_ld_median` so the
difference is auditable in the response itself.

**Figures are always annual** and `salary_period` is always `YEAR`.
ZipRecruiter leads 6 of 11 titles measured with an hourly figure, but its own structured
data is annual (`P1Y`) on 13 of 13 pages, including every hourly-preferring title. Driving
the row off the page's preference would make `median_salary` mean something different for
Truck Driver than for Data Scientist. All four frequencies ride along in
`ziprecruiter_pay_frequencies`, and the page's own preference is preserved as
`ziprecruiter_preferred_pay_period`.

**What only this source has:** p25 and p75, an 11-bucket salary histogram giving the real
distribution, a 59 to 64 row state and province table, the 10 top-paying cities, 5 related
titles with pay deltas, all four pay frequencies on one row, the count of open listings
behind the figures, the spread level, ZipRecruiter's own summary prose and its generated
FAQ.

**Coverage is national and state or province level only.** A city returns a 400 error
before any request is made: ZipRecruiter publishes salary pages at state and national
level. `location_type` and `years_of_experience`, which JSearch offers, return a 400 error
here.

A title ZipRecruiter has never published, or a title with no figures for the requested
state, returns a 404 error. Coverage is decided per title and state pair:
`Software-Engineer` has an Ontario page, `Truck-Driver` a national page only.

**Billing:** `Requests` counts 1 per call.

## Output

A single data row per request containing the full ZipRecruiter pay distribution for the requested job title and location: annual min, median, and max salary (with true median parsed from the page data layer, not the mislabeled JSON-LD mean), p25 and p75 percentiles, an 11-bucket salary histogram, average salary, national average for comparison, pay frequency breakdowns, top paying cities, related job titles, total job listings count, spread level, and metadata including when the data was last updated.

## Request schema (JSON Schema)

```json
{
 "type": "object",
 "required": [
  "job_title"
 ],
 "properties": {
  "fields": {
   "type": "string",
   "description": "Comma-separated projection over the salary row, for example\n`median_salary,salary_period`. `job_title` and `location` are always included. An\nunknown field name returns a 400 error; use the salary row's own field names."
  },
  "country": {
   "enum": [
    "us",
    "ca"
   ],
   "type": "string",
   "default": "us",
   "description": "Country market the figures belong to. Only `us` and `ca` are served.\n\n**Allowed values:** `us`, `ca`\n\n**Default:** `us`"
  },
  "location": {
   "type": "string",
   "description": "US state, Canadian province or country, spelled out in full, for example `New York` or\n`Alberta`. Two-letter codes and city names return a 400 error: ZipRecruiter publishes\nfigures at state and national level. Omit for the national figure.\n\n**Default:** nationwide"
  },
  "job_title": {
   "type": "string",
   "example": "Registered Nurse",
   "description": "Job title to price, for example `Registered Nurse`. Matched against ZipRecruiter's own\nsalary pages, so a title it does not publish returns a 404 error."
  }
 }
}
```

## Response schema (JSON Schema)

```json
{
 "type": "object",
 "required": [
  "status",
  "request_id",
  "parameters",
  "data"
 ],
 "properties": {
  "data": {
   "type": "array",
   "items": {
    "type": "object",
    "required": [
     "location",
     "job_title",
     "min_salary",
     "max_salary",
     "median_salary",
     "min_base_salary",
     "max_base_salary",
     "median_base_salary",
     "min_additional_pay",
     "max_additional_pay",
     "median_additional_pay",
     "salary_period",
     "salary_currency",
     "salary_count",
     "salaries_updated_at",
     "publisher_name",
     "publisher_link",
     "confidence",
     "ziprecruiter_average_salary",
     "ziprecruiter_national_average_salary",
     "ziprecruiter_p25_salary",
     "ziprecruiter_p75_salary",
     "ziprecruiter_pay_frequencies",
     "ziprecruiter_salary_histogram",
     "ziprecruiter_salaries_by_state",
     "ziprecruiter_top_paying_cities",
     "ziprecruiter_related_titles",
     "ziprecruiter_total_listings",
     "ziprecruiter_spread_level",
     "ziprecruiter_location_granularity",
     "ziprecruiter_preferred_pay_period",
     "ziprecruiter_job_title_searched",
     "ziprecruiter_salaries_updated_at_text",
     "ziprecruiter_summary",
     "ziprecruiter_faq",
     "ziprecruiter_json_ld_median"
    ],
    "properties": {
     "location": {
      "type": "string",
      "example": "United States",
      "nullable": true,
      "description": "The scope the figures describe: the state or province name on a state request, and the\ncountry name (`United States`, `Canada`) on a national one. Read from the page's own\nscope field."
     },
     "job_title": {
      "type": "string",
      "example": "RN",
      "nullable": true,
      "description": "ZipRecruiter's canonical title for the page, which can differ from what was sent:\n`Registered Nurse` resolves to `RN`. What was sent is echoed in `parameters`, and the\npage's own raw title is `ziprecruiter_job_title_searched`."
     },
     "confidence": {
      "type": "string",
      "example": null,
      "nullable": true,
      "description": "Always `null`. ZipRecruiter states no estimator confidence. `ziprecruiter_spread_level`\ndescribes how widely the top-city figures spread, which is a different thing."
     },
     "max_salary": {
      "type": "number",
      "example": 131000,
      "nullable": true,
      "description": "90th percentile annual pay, the top of the middle 80%."
     },
     "min_salary": {
      "type": "number",
      "example"
… (truncated)
```

## More

- Live health (JSON, refreshed every minute): https://www.zero.xyz/c/agents-openwebninja-com-ziprecruiter-estimated-salary-0eeed7f8/health.json
- [Zero catalog index](https://www.zero.xyz/llms.txt)
- [Other services from agents.openwebninja.com](https://www.zero.xyz/host/agents.openwebninja.com/llms.txt)
