Advertised pay, its trend, and market benchmarks
Advertised pay, its trend, and market benchmarks
Call: GET /v1/pay
Price: Uses credits (see each response's X-Credits-Charged)
Available: soft launch
Contract: https://api.gwix.ai/docs/reference
What it's for
Advertised pay percentiles (p10-p90) by period, with optional change over time and market benchmarks.
Use it when
- "What does a nurse earn in Denver?" (advertised pay)
- "How does our pay compare with the market?" - use
benchmark.
Inputs
| Name | In | Type | Required | Meaning |
|---|---|---|---|---|
company_id |
query | list of string | One or more brand-level company IDs (from the company lookup). Use only one of company_id, parent_company_id or ticker. |
|
parent_company_id |
query | string | Ultimate parent company ID; combines all its brands into one group. Uses current ownership for every period (meta.ownership_basis). |
|
ticker |
query | string | Stock ticker (MAR). Resolves to the listed company and is treated as parent_company_id (the whole corporate group, current ownership). Add country when the ticker is used in more than one country. |
|
split |
query | string: brand | With parent_company_id, return one row per brand instead of one group row. |
|
location_id |
query | list of string | One or more location IDs - country (US), state (US-CO) or metro (mtr_…). A city or postcode resolves to its metro through the location lookup. |
|
country |
query | string | ISO 3166-1 alpha-2 country code. | |
industry_id |
query | list of string | One or more 4-digit NAICS industry groups. Statistics are built at 4 digits only, with no 2-, 3- or 6-digit rollups; to cover a wider sector, list its 4-digit codes or aggregate the results. | |
role_id |
query | list of string | One or more standardized role IDs. Use one occupation filter. Role with a company, parent company or ticker is not pre-computed at launch (501); role with an industry is. | |
job_family_id |
query | list of string | One or more job families (the broadest occupation level). | |
sub_job_family_id |
query | list of string | One or more sub-job families (the level between job family and role). | |
soc_id |
query | list of string | One or more US SOC 2018 detailed occupations (soc_15-1252), computed from each ad's SOC code. Geography and job level only; with a company, parent, ticker or industry filter 501. |
|
occupation_code |
query | list of string | One or more occupation codes as <system>:<code>: soc_2018:15-1252, soc_2010:15-1132, onet_soc:15-1252.00, isco_08:2512, esco:2512.4 (short names soc, soc2010, onet, isco are accepted; a bare 15-1252 is SOC 2018). Each code resolves to the SOC 2018 detailed occupations it maps to (see crosswalk); the response has one row set per SOC occupation, each with matched_from, and meta.occupation_codes lists every mapping, including SOC occupations without data. A partial match covers only part of the SOC occupation; rows are never pooled across SOC occupations. Unknown code 422 unknown_code; a code whose SOC occupations have no ads 422 no_data_for_code. Same limits as soc_id. |
|
job_level_id |
query | list of string | One or more job levels (seniority). | |
interval |
query | string: week | month | quarter |
start_period |
query | string | First period label, e.g. 2025-01, 2025-Q2, 2025, 2025-W14. |
|
end_period |
query | string | ||
compare |
query | string: previous | 12_month | |
pay_unit |
query | string: annual | hourly | |
currency |
query | string: USD | local | |
percentiles |
query | list of string: p10 | p25 | p50 |
benchmark |
query | list of string: national | industry | local |
benchmark_industry_id |
query | string | 4-digit NAICS industry group for the industry benchmark. Default is the subject's own 4-digit industry. | |
cursor |
query | string | Opaque cursor from paging.next_cursor. |
|
limit |
query | integer | Rows per page, 1 to 1,000 (default 100). Requests needing more than 1,000 rows in total answer 501; narrow them. |
Headers: Idempotency-Key, GWIX-Agent-Id, GWIX-On-Behalf-Of.
What comes back
rows, meta, paging.
If an input is ambiguous: status: needs_disambiguation with candidates, the missing input and a retry_with template (no charge).
Rules
Advertised pay. Pay figures are advertised pay: what employers state in job ads. They are not what any employer actually pays, and not official statistics.
Pay units. Pay is annual by default; hourly = annual / 2,080. Statistics are computed in each country's own currency and returned in USD at the period's average exchange rate (fx_rate, units per USD), or in the country's currency with currency=local (the row's currency names it). Record pay is in USD. The rates used are published by list_fx_rates.
Minimum sample sizes. A pay percentile is shown only when enough ads state pay: p50 needs 10, p25 and p75 need 20, p10 and p90 need 50. Below that the value is null and listed in suppressed with reason below_minimum.
Periods. Periods are whole Monday-Sunday weeks. A month is 4 weeks, a quarter 13, a year 52, anchored to complete weeks ending on or before the period's last day. There are no partial periods; the current week is never included until it is complete. Ask for periods with interval plus start_period and end_period labels (custom date ranges are not available at launch).
Zeros and coverage. coverage_status: covered means GWIX sources were watching, so a zero means no ads were observed. not_covered means GWIX cannot say; values are null. Periods and named values are always returned, even when zero; unnamed zero rows are omitted and counted in meta.zero_rows_omitted.
Geography. Statistics are built at metro level. Location filters work at metro, state and country at launch; a city or postcode resolves to its metro. Every row is specific to one country.
Company groups and ownership. Company groups (parent_company_id, ticker) use current ownership for every period (meta.ownership_basis: current). A brand acquired last year is counted in its current parent's history.
IDs, not plain text. Statistics take IDs. Resolve names to IDs first with lookup (company, ticker, place, city, postcode, job family, sub-job family) or send occupation codes (occupation_code). Plain-text filters (title, company, location, job_family) are not available at launch and answer 501 with a retry_with lookup link.
Request size. A request answers up to 1,000 rows and 200 filter combinations inline; larger requests answer 501 job_required - narrow the filters or periods. Asynchronous jobs come later.
Provenance and reproducibility. Every response carries meta.provenance (the data release and its last complete week) and a ready-made meta.citation. Statistics come from the live weekly release; each response names it.
Notes
benchmarkcompares the subject with the same occupation (role, sub-job family, job family or SOC occupation) in the national, industry, local or local-industry market; it requires an occupation filter. SOC occupations take national and local only.- The industry benchmark uses the subject's 4-digit NAICS industry group, with no step-down to wider sectors; when counts are too low its percentiles are suppressed.
- Industry filters are 4-digit NAICS only; for a wider sector, list its 4-digit codes or aggregate the results.
- Role with a company, parent company or ticker is not pre-computed at launch (501); use sub-job family or job family with a company. Role with an industry is pre-computed.
- With
compareandbenchmarktogether, each market block carriesmarket_change_pctandgrowth_gap_pp. - Group percentiles pool every brand's ads.
- Weekly figures (
interval=week) are served for occupations, single companies (also brand by brand withsplit=brand) and industries on their own; for a parent company or ticker, a company or industry combined with an occupation, or industry benchmarks, use month, quarter or year (weekly returns 501). - Use
currency=localfor pay trends within a country (no exchange-rate effects) and USD to compare countries; always state the currency shown in the row. - Occupation codes: send
occupation_code=isco_08:2512(alsosoc_2018,soc_2010,onet_soc,esco) orsoc_id; you get one row set per SOC 2018 detailed occupation the code maps to, each withmatched_from. Apartialmatch covers only part of the SOC occupation - say so. Never add SOC rows together for pay. SOC occupations are by geography and job level only.
How to word results
- Cite GWIX using
meta.citation. - Say "advertised pay" or "pay in job ads", never "salary paid" or "what X pays".
- State the currency of every pay figure (the row's
currency). - Say "ads first posted", not "jobs" or "hires" or "openings filled".
- Report suppressed percentiles as "not enough ads stating pay to report", not as zero.
- Report zeros with
coveredas "no ads observed in GWIX sources", not "no jobs exist". - State the level actually used (e.g. metro, 4-digit NAICS) from
meta. - For company groups, say figures use current ownership.
- Never: present figures as official statistics or as an employer's actual pay.
- Never: name a data vendor; name datasets as they appear in
meta.provenance.
When it doesn't work
| Status | What to do |
|---|---|
| 202 | Still running: you have a job. Poll get_job or wait for a webhook. |
| 400 | Fix the request; invalid_params names each bad parameter. |
| 401 | Send a valid API key (Authorization: Bearer ...). |
| 402 | Not enough credits. The body lists packs and plans that would cover it. |
| 403 | Not allowed for this key or tier, a cap is reached, or the agent is unidentified; read type and detail. |
| 409 | Idempotency-Key reused with a different request, or the action conflicts with the current state. |
| 422 | Parameters conflict (e.g. two company filters) or use a reserved feature. |
| 429 | Rate or concurrency limit. Wait Retry-After seconds. |
| 503 | Temporarily unavailable; see get_status. |
Next steps
Example response
{
"rows": [
{
"period_label": "2026-Q3",
"period_start": "2026-06-29",
"period_end": "2026-09-27",
"country": "US",
"location": {
"id": "mtr_denver_aurora_co",
"name": "Denver-Aurora-Lakewood, CO",
"geo_level": "metro"
},
"role": {
"id": "rol_29-1141.00",
"name": "Registered Nurse"
},
"coverage_status": "covered",
"pay_unit": "annual",
"currency": "USD",
"fx_rate": 1,
"ads_with_pay": 412,
"percentiles": {
"p10": 71500,
"p25": 78200,
"p50": 86400,
"p75": 95100,
"p90": 104800
},
"suppressed": [],
"market": [
{
"scope": "national",
"ads_with_pay": 18240,
"percentiles": {
"p10": 66800,
"p25": 74100,
"p50": 82900,
"p75": 93600,
"p90": 106200
},
"gap_pct": {
"p10": 7.0,
"p25": 5.5,
"p50": 4.2,
"p75": 1.6,
"p90": -1.3
}
}
]
}
],
"meta": {
"request_id": "gwx_req_9d2p4q",
"provenance": {
"datasets": [
{
"id": "us-postings-core",
"last_complete_week": "2026-10-25"
}
],
"data_release": "2026-W43",
"methods": {
"pay_consolidation": "2.3"
},
"taxonomy_version": "utb-1.0",
"computed_at": "2026-10-28T14:05:51Z"
},
"citation": {
"text": "GWIX advertised pay, Registered Nurses, Denver metro, 2026-Q3, release 2026-W43, retrieved 28 Oct 2026 (gwx_req_9d2p4q)",
"url": "https://api.gwix.ai/v1/requests/gwx_req_9d2p4q"
}
},
"paging": {
"next_cursor": null
}
}