Occupation data

4 endpoints, generated from the API's own OpenAPI document.

GET /v2/occupation/series

Occupation series

Credentials: API key or App session. Operation id dataOccupationSeries.

Occupation time series (OEWS vocabulary): one series per code x area x ownership over a range of periods, every period present (sparse means zero: a period with no source row is a true 0). Areas: a list gives one series per area; a lone cluster:<id> or msa:<code>, or sum=areas, gives one summed series (meta.geography.region says what is in it). Codes: a list or level gives one series per code; cluster:<id> or sum=codes gives one summed series. Summed values follow one rule set: values sum (in exact cents), wage_mean is recomputed from the summed wage bill and employment, quarters_present takes the minimum over members with data. Occupation values carry no per-value provenance: read meta.provenance_boundaries. Nation and state series are open to every account; county series need the county in the organization's active county set (a partial exclusion is counted in meta.geography.excluded). One guard of 400,000 cells per request, a cell being one series at one period; past it, page with cursor and limit. Callers: an API key (live or sandbox) or a session, any member role. A live API key needs an organization whose plan includes the API add-on (403 plan_required, entitlement api_access) and that is not past due; a sandbox key needs neither and reads synthetic data. Gates, in order: authentication, the per-credential and per-organization rate ceilings, the global admission gate (429 server_busy; a refused request is not metered), then the one entitlement gate (API access, vintage access, quarterly access, export access and the monthly points allowance). Metering: a JSON request on a SESSION is never metered; a JSON request on an API key, and every CSV or XLSX export on any credential, spends points from the organization's monthly points allowance (the plan's figure, GET /v2/plans; a sandbox key spends a separate sandbox allowance). During a trial, an export whose geography lies wholly inside the trial county is free of the points allowance and of the export entitlement. Side effects: one usage record per request (with the points emitted, the vintage read and the counties read), the member's last-active time, and, when a key is refused for its rate or the points allowance, a notice to the key's holder and the organization's admins at most once a day. A session request records the person's presence (it cancels an open ownership claim on an organization they own). Representations (spec sections 10 and 10a): JSON by default. CSV with Accept: text/csv or a .csv suffix on the path (/v2/occupation/series.csv); the suffix wins over Accept. The as-shown XLSX workbook with Accept: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet or an .xlsx suffix (/v2/occupation/series.xlsx). CSV is long format, one line per series-point (the series' key columns, then the point's columns), headers exactly the JSON field names in JSON order, values exactly as JSON writes them (null as an empty field, booleans true/false), RFC 4180 quoting, CRLF, UTF-8 without a byte-order mark; a text cell a spreadsheet would read as a formula starts with '. A # comment preamble precedes the header: taigan-csv, endpoint, the sandbox warning (sandbox only), vintage, vintage_label, source (the attribution to keep), generated_at, granularity, provenance_boundaries, geography, the region lines (summed areas only), count, encoding. The workbook has a Table sheet (full-precision values under display number formats, codes stored as text) and a Source sheet (attribution, vintage, the shape and grid state in words). The as-shown parameters (shape, by_period, columns, sort, filters, computed, total) shape the workbook only and are 400 xlsx_only_parameter on JSON or CSV. Files are named <domain>-<endpoint>-<vintage>-<UTC timestamp>.csv (or .xlsx). Every problem, on any representation, is application/problem+json.

Parameters

socquery · string
Comma list of SOC codes (29-1141,29-1171), one series per code in the taxonomy's nested order; or cluster:<id>, one of the organization's soc code clusters, alone, summed into ONE series named with the cluster's name. With sum=codes any non-overlapping list sums into one series labelled sum(<n>); an ancestor and its descendant together are refused. Default (neither soc nor soc_level): the level-0 total (00-0000). Not with soc_level.
soc_levelquery · string
Every code at one hierarchy level (0 Total, 1 Major group, 2 Minor group, 3 Broad occupation, 4 Detailed occupation); or all, every code at every level in nested order (a browse: parents and children together, never summed); or leaf, the codes that are the parent of no code. all and leaf need the plan's full depth. Not with soc. (pattern ^(\d+|all|leaf)$)
areasquery · string
Comma list of area codes ("00" nation, "22" state, "22073" county), state:XX (expands to the state's counties, INCLUDING its "Unknown Or Undefined" catch-all — O34), cluster:<id> (the caller's own fips-kind cluster), or msa:<code> (the MSA's member counties at the latest map, never a catch-all). T19: a cluster or an MSA ALONE is one summed series named by the cluster's name or the MSA's title; BESIDE other tokens it contributes its counties to the one summed area and requires sum=areas (else 400 area_sum_required). A summed selection may not overlap: 400 area_overlap {pair}. A retired MSA is 400 retired_msa naming its successors. areas=region:<id> is RETIRED (404 region_retired, D44). The county universe is 3,194 areas: 3,144 counties and county-equivalents plus the 50 per-state catch-alls holding employment not attributable to a specific county — ordinary areas in the data, included in state and national totals, purchasable, but with no geographic extent (is_catchall on /v2/account/counties governs presentation only: never in an msa: expansion, never a /map row, never a public data page).
regionquery · stringdeprecated
RETIRED: any value is 404 region_retired. Use areas=cluster:<id>.
combinequery · "sum"deprecated
Deprecated alias of sum=areas: sums the areas list into one series. Only sum is defined; not with sum.
sumquery · string
Uniform summing (D37-FINAL): a comma-separated list of the axes to collapse — codes, areas, and (v0.11) ownership, which sums an explicit ownership= subset into ONE series labelled with the subset (e.g. local,private); with order=ownership it is a 400. Any NON-overlapping code set sums; an ancestor/descendant pair is a 400 naming the pair. cluster:<id> references imply summing. R5 semantics: values sum; disclosure max; reliability employment-weighted; restatement OR; avg_pay/wage_mean from summed numerator/denominator once-rounded; quarters_present min over members with data.
ownershipquery · string
Comma list of fed, state, local, private (or the QCEW codes 1, 2, 3, 5). Default: one series summed across all four (ownership: "all"). An explicit list gives one unsummed series per ownership, or with sum=ownership one series summed over the subset.
granularityquery · "annual" | "quarterly"
annual (default) or quarterly. Quarterly needs the plan's quarterly access (403 plan_required, quarterly_access). (default "annual")
fromquery · string
Start period, inclusive — "2004" (annual) or "2004q1" (quarterly); must match granularity=. (pattern ^\d{4}(q[1-4])?$)
toquery · string
End period, inclusive — same format and granularity as from=. (pattern ^\d{4}(q[1-4])?$)
measuresquery · string
Comma list of the measures to return, from emp, wage_mean (default all). Labels are always returned on industry points.
orderquery · "soc" | "area" | "ownership"
The major dimension of series ORDER: a sort key only, never a grouping; the code axis is never collapsed. ownership also splits the default summed ownership into one series per ownership. Not with group. (default "soc")
groupquery · "soc" | "area" | "ownership"deprecated
Deprecated alias of order; removal would be a new major version, announced first. Not with order.
strictquery · "true"
true: any county the entitlement excludes makes the request 403 geography_excluded (listing up to 50) instead of a 200 with meta.geography.excluded above zero; while the organization is past due, 403 plan_required with past_due instead.
cursorquery · string
Paging: the opaque meta.pagination.next_cursor of the previous page. A page is a slice of the series list in the order the response emits; the cell guard measures the page. Giving cursor or limit turns paging on; with neither the whole result comes in one response.
limitquery · integer
Paging: series per page, 1 to 5,000 (1,000 when only cursor is given). Absent with no cursor: unpaged. (default 1000; 1 to 5000)
vintagequery · string
The vintage to read (default: the current one). Pinning it makes the answer immutable and cacheable for a year. (pattern ^(\d{8}|sandbox-\d{4}\.\d+)$)
shapequery · "snapshot" | "by-period"
XLSX only. by-period: one row per series with a column per period per point field (emp_2019, ..., disclosure_2019, reliability_2019). Anything else: the snapshot, one row per series showing its last period.
by_periodquery · "1"
XLSX only. 1 is the same as shape=by-period.
columnsquery · string
XLSX only. The table's columns in order, comma-separated: response fields, title (the code's name), the computed columns, and on /series lq. Default: everything. On industry sheets disclosure and reliability (and lq_disclosure beside lq) are always inserted after the values they qualify, whatever is named; those forced labels are not metered. An unknown name is 400 unknown_column.
sortquery · string
XLSX only. <column>:<asc|desc> (default desc) on a snapshot value or a computed column; nulls last. (pattern ^[^:]+(:(asc|desc))?$)
filtersquery · string
XLSX only. Comma list of <column>:<ge|gt|le|lt>:<number>, or disclosure:published (keeps disclosure 0; in the by-period shape, every period exported must be published). A row with no value for a filtered column never passes; rank is not filterable.
computedquery · string
XLSX only. Comma list of emp_start (first period's employment), change_abs (last minus first), change_pct ((last minus first) / first x 100; null when first is 0), share (last / sum of every returned row's last x 100, taken before filtering), rank (1, 2, 2, 4 over the filtered rows, highest first, on the sort column or employment). Only emp_start is metered.
totalquery · string
XLSX only, /series: cluster:<id>, a code cluster of this domain whose summed series is appended beneath the rows as the table's total row (Total: <name>), not filtered, sorted or ranked; metered as its values are. (pattern ^cluster:[0-9a-fA-F-]{36}$)
X-DTG-Orgheader · string (uuid)
Session only: the organization the request acts in, when the person belongs to more than one (default: their own organization). An API key is bound to its organization; on a key this header must name that organization or be omitted. A malformed id, or an organization the caller is not an active member of, is 404 not_found.

Responses

200 success

The series.

As application/json, text/csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.

X-RateLimit-Limit header
The per-minute request ceiling that applies.
X-RateLimit-Remaining header
Requests left in the current window.
X-RateLimit-Reset header
Seconds until the window refills: on every answer, the time until the bucket is full again; on a refusal, until a request is allowed (as Retry-After).
X-DTG-Sandbox header
Present, with the value true, on every response to a sandbox API key, success and problem alike; it is set as soon as the credential is known. Every value in a sandbox response is synthetic and must never be published as data. Absent for live keys and sessions.
Content-Disposition header
CSV and XLSX only: attachment; filename="<domain>-<endpoint>-<vintage>-<UTC timestamp>.csv" (or .xlsx), e.g. industry-series-20260913-20260927T184215Z.csv.
Cache-Control header
CSV and XLSX only: private, no-store.
Access-Control-Expose-Headers header
CSV and XLSX only: Content-Disposition, so a cross-origin first-party app can read the file name.
dataarray of DataSeriesOccupationalways present
The series (this page of them, when paged), in the order order sets.
data[].socstringalways present
The SOC code as a string (29-1141), names on GET /v2/taxonomies/soc. On a summed code axis it is the label instead: the cluster's name for soc=cluster:<id>, or sum(<n>) for sum=codes over n codes.
data[].areastringalways present
The area: a FIPS code as a zero-padded string (00 the nation, 22 a state's own row, 22073 a county; a code ending 999 is that state's catch-all area, Unknown Or Undefined, holding employment QCEW cannot place in a county: a real, purchasable area included in state and national totals, with no geographic extent). On a summed-area series it is the label instead: the cluster's name or the MSA's title when one was asked for alone, otherwise custom region; meta.geography.region lists what was summed. The response carries codes, not area names: names are on GET /v2/taxonomies/areas.
data[].ownershipstringalways present
The ownership the series covers: all (the default: federal, state and local government and private summed), one of fed, state, local, private (an unsummed series, from an explicit ownership= list or order=ownership), or with sum=ownership the summed subset as tokens in code order, e.g. local,private.
data[].pointsarray of DataOccupationPointalways present
One point per period of the requested range, in period order, every period present (dense).
data[].points[].periodstringalways present
The period: 2021 (annual) or 2021q3 (quarterly), per granularity. (pattern ^\d{4}(q[1-4])?$)
data[].points[].empnumber
Occupation employment in jobs, on the same basis as industry employment (occupation employment is reconciled to DTG industry employment). A quarter is its average; an annual value is the sum of its quarters divided by 4. Present when measures includes emp.
data[].points[].wage_meannumber or null
Mean earnings per WORKER for the period, in US dollars: the cell's wage bill divided by its employment, rounded once (annual: close to the OEWS annual mean wage; quarterly: the quarter's earnings per worker). OEWS annualizes hourly pay at 2,080 hours; an hourly figure (annual divided by 2,080) is presentation arithmetic, not a measured hourly wage. Occupation wage totals do not sum to industry earnings, by construction; only employment reconciles. Null when employment is zero. Present when measures includes wage_mean.
data[].points[].quarters_presentinteger
Annual points only: how many of the year's quarters have a source row (4 = complete year, 0 = a true-zero year). On a summed series, the minimum over the summed members that have data in the year. (0 to 4)
metaDataMetaalways present
The meta of every v2 envelope (spec section 3). It is written after data on the wire. Clients must tolerate unknown fields.
meta.vintagestringalways present
The vintage the answer was read from: the internal build id (20260913; a sandbox key's are sandbox-2026.1). Cite it and pass it back as vintage= to reproduce the answer exactly: a released vintage never changes. Its public name, such as DTG 2026.3, is the label on GET /v2/vintages.
meta.sandboxtrue
Present, and true, only when the answer was read from a sandbox vintage: every value is synthetic and must never be published. Absent otherwise.
meta.generated_atstring (date-time)always present
When this response was produced, UTC, to the second (2026-09-27T18:42:15Z).
meta.granularity"annual" | "quarterly"
Data routes only: the period granularity of the points (on /map, quarterly when qtr was given). Absent on taxonomy responses.
meta.provenance_boundariesDataProvenanceBoundariesalways present
Where the vintage's published record ends and estimation begins, recorded per vintage at build time and returned on every envelope. Compare a point's period against these ranges to know what kind of value it is. For OCCUPATION data this is the only provenance there is: occupation values carry no per-value label, so a period inside actuals is published-basis, inside ces carried forward, inside projections projected. Industry values also carry their own disclosure code, which is authoritative for the value. The ranges move with each release; read them, never hard-code them.
meta.provenance_boundaries.actualsobjectalways present
Quarters published by BLS in QCEW (the Quarterly Census of Employment and Wages). Industry values here have disclosure 0 (published) or 1 (estimated by DTG where QCEW withheld the value).
meta.provenance_boundaries.actuals.fromstringalways present
First quarter of the range, lower case, e.g. 2004q1. (pattern ^\d{4}q[1-4]$)
meta.provenance_boundaries.actuals.tostringalways present
Last quarter of the range, lower case, e.g. 2025q4. (pattern ^\d{4}q[1-4]$)
meta.provenance_boundaries.cesobjectalways present
Quarters after the latest QCEW release, carried forward from the CES and SAE monthly employment surveys (their rate of change applied to DTG's base, not their levels). Industry values here have disclosure 2.
meta.provenance_boundaries.ces.fromstringalways present
First quarter of the range, lower case, e.g. 2004q1. (pattern ^\d{4}q[1-4]$)
meta.provenance_boundaries.ces.tostringalways present
Last quarter of the range, lower case, e.g. 2025q4. (pattern ^\d{4}q[1-4]$)
meta.provenance_boundaries.projectionsobjectalways present
Projected quarters, out to the end of the period axis. Industry values here have disclosure 3.
meta.provenance_boundaries.projections.fromstringalways present
First quarter of the range, lower case, e.g. 2004q1. (pattern ^\d{4}q[1-4]$)
meta.provenance_boundaries.projections.tostringalways present
Last quarter of the range, lower case, e.g. 2025q4. (pattern ^\d{4}q[1-4]$)
meta.geographyobject
Data routes only. Absent on taxonomy responses.
meta.geography.requestedintegeralways present
/series and /lq: the number of distinct areas the request resolved to before entitlement (a state:XX, msa: or cluster: token counts its counties; a plain state code counts 1; no areas counts the nation as 1). /map: the counties of the areas restriction, or without one the counties with a row for this code and period (catch-alls never counted). (at least 0)
meta.geography.returnedintegeralways present
Areas (on /map, counties) that passed entitlement and are in the answer. On a summed request these are the areas inside the one summed series. (at least 0)
meta.geography.excludedintegeralways present
Requested counties the organization's entitlement removed (outside the active county set, or every county while the subscription is past due). On /map a requested-versus-returned gap beyond excluded means counties with no data for the code and period, not an exclusion. (at least 0)
meta.geography.regionDataRegion
Present only on a request whose areas were summed into one series (a lone cluster:<id> or msa:<code>, or sum=areas / combine=sum): what the summed area is made of. The CSV preamble prints it as # region: lines and the workbook's Source sheet as Region rows.
meta.geography.region.labelstringalways present
The summed series' area label: the cluster's name or the MSA's title when one was asked for alone, otherwise custom region.
meta.geography.region.areasarray of DataRegionAreaalways present
Each area as requested, in request order.
meta.geography.region.areas[].tokenstringalways present
The token exactly as requested: 22073, 22, 00, state:22, msa:35380, cluster:<id>.
meta.geography.region.areas[].kind"nation" | "state" | "county" | "stateAll" | "msa" | "cluster"always present
What the token is: the nation (00), a state's own row (22), a county, stateAll (a state:XX token: every county of the state, its catch-all included), an MSA, or one of the organization's area clusters.
meta.geography.region.areas[].codestringalways present
The area's code: a FIPS code for a county or state (the 2-digit state code for stateAll), or the token itself, lower-cased, for msa:<code> and cluster:<id>.
meta.geography.region.areas[].namestring or nullalways present
The area's name: the vintage taxonomy's title for a county or state, the MSA's title, or the cluster's name. Null where the taxonomy has none.
meta.geography.region.areas[].resolvedarray of stringalways present
The area codes this token contributed to the sum after entitlement. A county the plan does not cover appears in no list; geography.excluded counts it.
meta.geography.region.resolvedarray of stringalways present
Every area code summed, deduplicated, in request order, after entitlement.
meta.paginationDataPaginationalways present
meta.pagination.cursorstring or nullalways present
The cursor this page was requested with; null for the first page or an unpaged request.
meta.pagination.next_cursorstring or nullalways present
Pass as cursor to get the next page. Null on the last page and on every unpaged response, so null always means there is nothing more.
meta.pagination.countintegeralways present
Rows in data on this response: series on /series and /lq, rows on /map, items on a taxonomy. (at least 0)

400 error

Bad request. Codes: unknown_parameter (a query parameter this route does not take; parameters[] names them), repeated_parameter (a parameter given more than once; parameter), bad_request (more than 50 query parameters); xlsx_only_parameter (an as-shown parameter on a JSON or CSV request; parameters[]); bad_request for a malformed vintage (a live key or a session takes YYYYMMDD, a sandbox key sandbox-YYYY.N; an id shaped for the other scope is refused the same way, so neither scope can probe the other); bad_request also for: a bad granularity; a from or to that is not YYYY or YYYYqN, a quarterly one without granularity=quarterly, one outside the vintage's period axis, or from after to; a bad cursor, a cursor past the end, or a limit outside 1 to 5,000; a bad sum token, or sum and combine together, or a combine other than sum; a malformed or unknown code, both soc= and soc_level=, a soc_level that is not an integer, all or leaf, a level with no codes, sum=codes with soc_level=all or leaf, an overlapping code set with sum=codes (the pair is named: "481 is contained in 48"), cluster:<id> not alone in soc=, or a malformed cluster reference; a bad ownership token; an unknown area, a malformed area code, a state:XX with no counties, a malformed msa: token, an unknown MSA, or a malformed cluster: token in areas; a bad measures entry; a bad order, or order and group together; sum=ownership with order=ownership; bad_cluster_ref (a cluster: id that is not a 36-character UUID); area_sum_required (an msa: or cluster: token beside other areas without sum=areas; token); area_overlap (a summed area list whose members overlap, D67; pair); retired_msa (an MSA with no counties in the current map; msa, retired_after, successors); cell_cap (the request, or the page, is more than 400,000 cells, a cell being one series at one period; cells, cap: narrow the areas, codes or period range, or page with cursor and limit); series_cap (more than the series cap per request, 10,000 by default; series, cap); XLSX only: unknown_column (a columns, sort or filters name the table does not have; column, parameter), column_not_filterable (a filter on rank), bad_total (a total that is not cluster:<uuid>), xlsx_row_ceiling (more rows than a sheet holds; rows, max_rows, alternatives), xlsx_column_ceiling (more columns than a sheet holds; columns, max_columns), bad_request for a malformed sort or filters entry.

As application/problem+json.

X-DTG-Sandbox header
Present, with the value true, on every response to a sandbox API key, success and problem alike; it is set as soon as the credential is known. Every value in a sandbox response is synthetic and must never be published as data. Absent for live keys and sessions.

401 error

Unauthenticated. Codes: unauthenticated (no credential, or an API key or session that is unknown or expired); key_revoked (an API key that was revoked: revoked_at, and reason -- holder, admin, member_removed, seat_removed, api_disabled, addon_ended, account_closed, org_closed, retired, or null for a key revoked before T110 -- and a sentence saying when and why).

As application/problem+json.

403 error

Forbidden. Codes: no_membership (the account has no active organization membership), account_suspended (O56: the caller's own account is suspended by DTG; reason where recorded), org_suspended (the organization is suspended; organization {id, name} and, where recorded, reason), org_closed (the organization is closed; organization), wrong_instance (a key of the mode this process does not serve, for example a sandbox key on a live-only process; expected, and base_url naming the address that serves it); plan_required with entitlement naming what the plan lacks: api_access (a live API key on an organization without the API add-on), past_due (the organization's subscription is past due: every request on a live API key, every CSV or XLSX export, and county detail are paused; nation and state data on a session are not), vintage_access (a non-current vintage= on a plan without prior vintages), quarterly_access (granularity=quarterly on a plan without quarterly data), csv_access (a CSV or XLSX export on a plan without export, or a trial export outside the trial county), depth_cap_soc (a code deeper than the plan's soc depth cap; soc_level=all and leaf need the full depth); geography_excluded (every requested county is outside the organization's entitlement, or any is with strict=true; excluded_fips lists up to 50).

As application/problem+json.

X-DTG-Sandbox header
Present, with the value true, on every response to a sandbox API key, success and problem alike; it is set as soon as the credential is known. Every value in a sandbox response is synthetic and must never be published as data. Absent for live keys and sessions.

404 error

Not found. Codes: not_found (no such vintage in the caller's scope, or an X-DTG-Org header naming an organization the caller does not belong to), vintage_superseded (the pinned vintage was withdrawn and replaced by a correction; superseded_by names it; re-run against it), vintage_withdrawn (the pinned vintage was withdrawn and no correction exists yet; current names the vintage to use), region_retired (areas=region:<id> or the region parameter is retired: use cluster:<id>; replaced_by), cluster_not_found (a cluster:<id> that is not one of the organization's clusters of that kind), cluster_deleted (the cluster was deleted; cluster, restorable, restorable_until).

As application/problem+json.

X-DTG-Sandbox header
Present, with the value true, on every response to a sandbox API key, success and problem alike; it is set as soon as the credential is known. Every value in a sandbox response is synthetic and must never be published as data. Absent for live keys and sessions.

414 error

URI too long. Codes: uri_too_long (the request URL is longer than the configured maximum, 8,192 bytes by default).

As application/problem+json.

429 error

Too many requests. Codes: rate_limited (over the 120 requests a minute per credential or 300 a minute per organization; sandbox ceilings are lower; X-RateLimit-* say which), server_busy (the whole service is at capacity: every data slot and queue place is taken; retry_after; not metered, not a limit on the caller), sandbox_busy (sandbox keys only: the sandbox's shared concurrent-query maximum is reached; retry_after), points_exhausted (the organization's monthly points allowance is spent; Retry-After counts to the end of the UTC month; checked on API-key and export requests, never on session JSON), quota_exhausted (a monthly CSV or API request quota an operator has re-armed for this organization is spent; retired as a published limit). Every one carries Retry-After.

As application/problem+json.

Retry-After header
Seconds to wait before retrying.
X-RateLimit-Limit header
The per-minute request ceiling that applies.
X-RateLimit-Remaining header
Requests left in the current window.
X-RateLimit-Reset header
Seconds until the window refills: on every answer, the time until the bucket is full again; on a refusal, until a request is allowed (as Retry-After).
X-DTG-Sandbox header
Present, with the value true, on every response to a sandbox API key, success and problem alike; it is set as soon as the credential is known. Every value in a sandbox response is synthetic and must never be published as data. Absent for live keys and sessions.

500 error

Server error. Codes: internal_error (for example a vintage file the server cannot read).

As application/problem+json.

Problem codes: unknown_parameter (400), repeated_parameter (400), bad_request (400), xlsx_only_parameter (400), bad_cluster_ref (400), area_sum_required (400), area_overlap (400), retired_msa (400), cell_cap (400), series_cap (400), unknown_column (400), column_not_filterable (400), bad_total (400), xlsx_row_ceiling (400), xlsx_column_ceiling (400), unauthenticated (401), key_revoked (401), account_closed (401), org_closed (401), no_membership (403), account_suspended (403), org_suspended (403), org_closed (403), wrong_instance (403), plan_required (403), geography_excluded (403), not_found (404), vintage_superseded (404), vintage_withdrawn (404), region_retired (404), cluster_not_found (404), cluster_deleted (404), uri_too_long (414), rate_limited (429), server_busy (429), sandbox_busy (429), points_exhausted (429), quota_exhausted (429), internal_error (500). Every problem is a problem document.

GET /v2/occupation/map

Occupation choropleth values (annual or quarterly)

Credentials: API key or App session. Operation id dataOccupationMap.

Choropleth values for one SOC code (or a summed code set) and one year or quarter: a row per state for every account, and a row per county the organization is entitled to that has data, in FIPS order. A /map row is never the nation and never a per-state catch-all county; catch-alls are dropped before the entitlement count, so they never read as excluded. Counties outside the active county set (or all counties while the organization is past due) are left out and counted in meta.geography.excluded; there is no 403 for them unless strict=true is sent, which turns any exclusion into 403 geography_excluded (as on /series). areas restricts the map to the union of its tokens' counties (then no state rows); nothing is summed across areas. Never paged; no cell guard applies. Rows carry no labels. As on /series and /lq, the code (each member of a summed set or cluster) must be within the plan's depth cap (403 plan_required, entitlement depth_cap_naics or depth_cap_soc), and a qtr is a quarterly request: it needs the plan's quarterly access (403 plan_required, entitlement quarterly_access). Callers: an API key (live or sandbox) or a session, any member role. A live API key needs an organization whose plan includes the API add-on (403 plan_required, entitlement api_access) and that is not past due; a sandbox key needs neither and reads synthetic data. Gates, in order: authentication, the per-credential and per-organization rate ceilings, the global admission gate (429 server_busy; a refused request is not metered), then the one entitlement gate (API access, vintage access, quarterly access, export access and the monthly points allowance). Metering: a JSON request on a SESSION is never metered; a JSON request on an API key, and every CSV or XLSX export on any credential, spends points from the organization's monthly points allowance (the plan's figure, GET /v2/plans; a sandbox key spends a separate sandbox allowance). During a trial, an export whose geography lies wholly inside the trial county is free of the points allowance and of the export entitlement. Side effects: one usage record per request (with the points emitted, the vintage read and the counties read), the member's last-active time, and, when a key is refused for its rate or the points allowance, a notice to the key's holder and the organization's admins at most once a day. A session request records the person's presence (it cancels an open ownership claim on an organization they own). Representations (spec sections 10 and 10a): JSON by default. CSV with Accept: text/csv or a .csv suffix on the path (/v2/occupation/map.csv); the suffix wins over Accept. The as-shown XLSX workbook with Accept: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet or an .xlsx suffix (/v2/occupation/map.xlsx). CSV is long format, one line per row (fips, value), headers exactly the JSON field names in JSON order, values exactly as JSON writes them (null as an empty field, booleans true/false), RFC 4180 quoting, CRLF, UTF-8 without a byte-order mark; a text cell a spreadsheet would read as a formula starts with '. A # comment preamble precedes the header: taigan-csv, endpoint, the sandbox warning (sandbox only), vintage, vintage_label, source (the attribution to keep), generated_at, granularity, provenance_boundaries, geography, the region lines (summed areas only), count, encoding. The workbook has a Table sheet (full-precision values under display number formats, codes stored as text) and a Source sheet (attribution, vintage, the shape and grid state in words). The as-shown parameters (shape, by_period, columns, sort, filters, computed, total) shape the workbook only and are 400 xlsx_only_parameter on JSON or CSV. Files are named <domain>-<endpoint>-<vintage>-<UTC timestamp>.csv (or .xlsx). Every problem, on any representation, is application/problem+json. The CSV of the trial county's own map (areas wholly inside the trial county) is trial-scoped.

Parameters

socquery · stringrequired
One SOC code; or cluster:<id>, one of the organization's soc code clusters, summed per area; or with sum=codes a comma list of non-overlapping codes summed per area.
measurequery · "emp" | "wage_mean"
The one measure mapped (see DataIndustryPoint / DataOccupationPoint for meanings). (default "emp")
yearquery · integerrequired
The year, four digits, inside the vintage's period axis (GET /v2/taxonomies/periods). (1000 to 9999)
qtrquery · integer
A quarter of year: the map then shows that quarter (meta.granularity: quarterly). Absent: the annual value. (1 to 4)
ownershipquery · string
Comma list of fed, state, local, private (or 1, 2, 3, 5): the ownerships summed into each value. Default all four.
areasquery · string
Restrict the map to counties: any areas tokens (counties, a state code or state:XX for its counties, msa:<code>, cluster:<id>), several at once; the map shows the union of their counties, never summed, catch-alls never drawn. Tokens may not overlap (400 area_overlap). 00 alone means no restriction (the whole map); beside other tokens it overlaps them. region:<id> is 404 region_retired.
sumquery · string
codes: sum the comma list in soc into one value per area. areas is a 400 (a map is per area); ownership is accepted and has no effect (ownership is always summed here).
combinequery · "sum"deprecated
Accepted for symmetry with /series; combine=sum is always a 400 here (a map is per area).
strictquery · "true"
true: a county the organization's entitlement excludes is a 403 geography_excluded listing the excluded FIPS (cap 50), or the past-due refusal when the pause is the cause; without it they are left out and counted in meta.geography.excluded.
vintagequery · string
The vintage to read (default: the current one). Pinning it makes the answer immutable and cacheable for a year. (pattern ^(\d{8}|sandbox-\d{4}\.\d+)$)
shapequery · "snapshot" | "by-period"
XLSX only. by-period: one row per series with a column per period per point field (emp_2019, ..., disclosure_2019, reliability_2019). Anything else: the snapshot, one row per series showing its last period.
by_periodquery · "1"
XLSX only. 1 is the same as shape=by-period.
columnsquery · string
XLSX only. The table's columns in order, comma-separated: response fields, title (the code's name), the computed columns, and on /series lq. Default: everything. On industry sheets disclosure and reliability (and lq_disclosure beside lq) are always inserted after the values they qualify, whatever is named; those forced labels are not metered. An unknown name is 400 unknown_column.
sortquery · string
XLSX only. <column>:<asc|desc> (default desc) on a snapshot value or a computed column; nulls last. (pattern ^[^:]+(:(asc|desc))?$)
filtersquery · string
XLSX only. Comma list of <column>:<ge|gt|le|lt>:<number>, or disclosure:published (keeps disclosure 0; in the by-period shape, every period exported must be published). A row with no value for a filtered column never passes; rank is not filterable.
computedquery · string
XLSX only. Comma list of emp_start (first period's employment), change_abs (last minus first), change_pct ((last minus first) / first x 100; null when first is 0), share (last / sum of every returned row's last x 100, taken before filtering), rank (1, 2, 2, 4 over the filtered rows, highest first, on the sort column or employment). Only emp_start is metered.
totalquery · string
XLSX only, /series: cluster:<id>, a code cluster of this domain whose summed series is appended beneath the rows as the table's total row (Total: <name>), not filtered, sorted or ranked; metered as its values are. On /map and /lq it is 400 bad_total. (pattern ^cluster:[0-9a-fA-F-]{36}$)
X-DTG-Orgheader · string (uuid)
Session only: the organization the request acts in, when the person belongs to more than one (default: their own organization). An API key is bound to its organization; on a key this header must name that organization or be omitted. A malformed id, or an organization the caller is not an active member of, is 404 not_found.

Responses

200 success

The map rows.

As application/json, text/csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.

X-RateLimit-Limit header
The per-minute request ceiling that applies.
X-RateLimit-Remaining header
Requests left in the current window.
X-RateLimit-Reset header
Seconds until the window refills: on every answer, the time until the bucket is full again; on a refusal, until a request is allowed (as Retry-After).
X-DTG-Sandbox header
Present, with the value true, on every response to a sandbox API key, success and problem alike; it is set as soon as the credential is known. Every value in a sandbox response is synthetic and must never be published as data. Absent for live keys and sessions.
Content-Disposition header
CSV and XLSX only: attachment; filename="<domain>-<endpoint>-<vintage>-<UTC timestamp>.csv" (or .xlsx), e.g. industry-series-20260913-20260927T184215Z.csv.
Cache-Control header
CSV and XLSX only: private, no-store.
Access-Control-Expose-Headers header
CSV and XLSX only: Content-Disposition, so a cross-origin first-party app can read the file name.
dataarray of DataOccupationMapRowalways present
State rows and entitled county rows, in FIPS order.
data[].fipsstringalways present
A 2-digit state code (a state row) or a 5-digit county code. Rows are in FIPS order, each state's row before its counties. Never the nation, and never a per-state catch-all county (O34: a choropleth has nowhere to draw it). With areas= only county rows are returned. (pattern ^\d{2}(\d{3})?$)
data[].valuenumber or nullalways present
The selected measure: emp (annual: sum of quarters divided by 4) or wage_mean (wage bill over employment, rounded once; null when employment is zero).
metaDataMetaalways present
The meta of every v2 envelope (spec section 3). It is written after data on the wire. Clients must tolerate unknown fields.
meta.vintagestringalways present
The vintage the answer was read from: the internal build id (20260913; a sandbox key's are sandbox-2026.1). Cite it and pass it back as vintage= to reproduce the answer exactly: a released vintage never changes. Its public name, such as DTG 2026.3, is the label on GET /v2/vintages.
meta.sandboxtrue
Present, and true, only when the answer was read from a sandbox vintage: every value is synthetic and must never be published. Absent otherwise.
meta.generated_atstring (date-time)always present
When this response was produced, UTC, to the second (2026-09-27T18:42:15Z).
meta.granularity"annual" | "quarterly"
Data routes only: the period granularity of the points (on /map, quarterly when qtr was given). Absent on taxonomy responses.
meta.provenance_boundariesDataProvenanceBoundariesalways present
Where the vintage's published record ends and estimation begins, recorded per vintage at build time and returned on every envelope. Compare a point's period against these ranges to know what kind of value it is. For OCCUPATION data this is the only provenance there is: occupation values carry no per-value label, so a period inside actuals is published-basis, inside ces carried forward, inside projections projected. Industry values also carry their own disclosure code, which is authoritative for the value. The ranges move with each release; read them, never hard-code them.
meta.provenance_boundaries.actualsobjectalways present
Quarters published by BLS in QCEW (the Quarterly Census of Employment and Wages). Industry values here have disclosure 0 (published) or 1 (estimated by DTG where QCEW withheld the value).
meta.provenance_boundaries.actuals.fromstringalways present
First quarter of the range, lower case, e.g. 2004q1. (pattern ^\d{4}q[1-4]$)
meta.provenance_boundaries.actuals.tostringalways present
Last quarter of the range, lower case, e.g. 2025q4. (pattern ^\d{4}q[1-4]$)
meta.provenance_boundaries.cesobjectalways present
Quarters after the latest QCEW release, carried forward from the CES and SAE monthly employment surveys (their rate of change applied to DTG's base, not their levels). Industry values here have disclosure 2.
meta.provenance_boundaries.ces.fromstringalways present
First quarter of the range, lower case, e.g. 2004q1. (pattern ^\d{4}q[1-4]$)
meta.provenance_boundaries.ces.tostringalways present
Last quarter of the range, lower case, e.g. 2025q4. (pattern ^\d{4}q[1-4]$)
meta.provenance_boundaries.projectionsobjectalways present
Projected quarters, out to the end of the period axis. Industry values here have disclosure 3.
meta.provenance_boundaries.projections.fromstringalways present
First quarter of the range, lower case, e.g. 2004q1. (pattern ^\d{4}q[1-4]$)
meta.provenance_boundaries.projections.tostringalways present
Last quarter of the range, lower case, e.g. 2025q4. (pattern ^\d{4}q[1-4]$)
meta.geographyobject
Data routes only. Absent on taxonomy responses.
meta.geography.requestedintegeralways present
/series and /lq: the number of distinct areas the request resolved to before entitlement (a state:XX, msa: or cluster: token counts its counties; a plain state code counts 1; no areas counts the nation as 1). /map: the counties of the areas restriction, or without one the counties with a row for this code and period (catch-alls never counted). (at least 0)
meta.geography.returnedintegeralways present
Areas (on /map, counties) that passed entitlement and are in the answer. On a summed request these are the areas inside the one summed series. (at least 0)
meta.geography.excludedintegeralways present
Requested counties the organization's entitlement removed (outside the active county set, or every county while the subscription is past due). On /map a requested-versus-returned gap beyond excluded means counties with no data for the code and period, not an exclusion. (at least 0)
meta.geography.regionDataRegion
Present only on a request whose areas were summed into one series (a lone cluster:<id> or msa:<code>, or sum=areas / combine=sum): what the summed area is made of. The CSV preamble prints it as # region: lines and the workbook's Source sheet as Region rows.
meta.geography.region.labelstringalways present
The summed series' area label: the cluster's name or the MSA's title when one was asked for alone, otherwise custom region.
meta.geography.region.areasarray of DataRegionAreaalways present
Each area as requested, in request order.
meta.geography.region.areas[].tokenstringalways present
The token exactly as requested: 22073, 22, 00, state:22, msa:35380, cluster:<id>.
meta.geography.region.areas[].kind"nation" | "state" | "county" | "stateAll" | "msa" | "cluster"always present
What the token is: the nation (00), a state's own row (22), a county, stateAll (a state:XX token: every county of the state, its catch-all included), an MSA, or one of the organization's area clusters.
meta.geography.region.areas[].codestringalways present
The area's code: a FIPS code for a county or state (the 2-digit state code for stateAll), or the token itself, lower-cased, for msa:<code> and cluster:<id>.
meta.geography.region.areas[].namestring or nullalways present
The area's name: the vintage taxonomy's title for a county or state, the MSA's title, or the cluster's name. Null where the taxonomy has none.
meta.geography.region.areas[].resolvedarray of stringalways present
The area codes this token contributed to the sum after entitlement. A county the plan does not cover appears in no list; geography.excluded counts it.
meta.geography.region.resolvedarray of stringalways present
Every area code summed, deduplicated, in request order, after entitlement.
meta.paginationDataPaginationalways present
meta.pagination.cursorstring or nullalways present
The cursor this page was requested with; null for the first page or an unpaged request.
meta.pagination.next_cursorstring or nullalways present
Pass as cursor to get the next page. Null on the last page and on every unpaged response, so null always means there is nothing more.
meta.pagination.countintegeralways present
Rows in data on this response: series on /series and /lq, rows on /map, items on a taxonomy. (at least 0)

400 error

Bad request. Codes: unknown_parameter (a query parameter this route does not take; parameters[] names them), repeated_parameter (a parameter given more than once; parameter), bad_request (more than 50 query parameters); xlsx_only_parameter (an as-shown parameter on a JSON or CSV request; parameters[]); bad_request for a malformed vintage (a live key or a session takes YYYYMMDD, a sandbox key sandbox-YYYY.N; an id shaped for the other scope is refused the same way, so neither scope can probe the other); bad_request also for: a missing soc, several codes without sum=codes, a malformed or unknown code, an overlapping code set with sum=codes, or a malformed cluster reference; sum=areas or combine=sum (a map is per area); a bad sum token or sum with combine; a bad measure; a missing or malformed year, or one outside the vintage's period axis; a qtr other than 1 to 4; a bad ownership token; an unknown or malformed area token in areas; bad_cluster_ref (a cluster: id that is not a 36-character UUID); area_overlap (two areas tokens that share counties, D67; pair); retired_msa (an MSA with no counties in the current map; msa, retired_after, successors); XLSX only: unknown_column (a columns, sort or filters name the table does not have; column, parameter), column_not_filterable (a filter on rank), bad_total (a total that is not cluster:<uuid>), xlsx_row_ceiling (more rows than a sheet holds; rows, max_rows, alternatives), xlsx_column_ceiling (more columns than a sheet holds; columns, max_columns), bad_request for a malformed sort or filters entry.

As application/problem+json.

X-DTG-Sandbox header
Present, with the value true, on every response to a sandbox API key, success and problem alike; it is set as soon as the credential is known. Every value in a sandbox response is synthetic and must never be published as data. Absent for live keys and sessions.

401 error

Unauthenticated. Codes: unauthenticated (no credential, or an API key or session that is unknown or expired); key_revoked (an API key that was revoked: revoked_at, and reason -- holder, admin, member_removed, seat_removed, api_disabled, addon_ended, account_closed, org_closed, retired, or null for a key revoked before T110 -- and a sentence saying when and why).

As application/problem+json.

403 error

Forbidden. Codes: no_membership (the account has no active organization membership), account_suspended (O56: the caller's own account is suspended by DTG; reason where recorded), org_suspended (the organization is suspended; organization {id, name} and, where recorded, reason), org_closed (the organization is closed; organization), wrong_instance (a key of the mode this process does not serve, for example a sandbox key on a live-only process; expected, and base_url naming the address that serves it); plan_required with entitlement naming what the plan lacks: api_access (a live API key on an organization without the API add-on), past_due (the organization's subscription is past due: every request on a live API key, every CSV or XLSX export, and county detail are paused; nation and state data on a session are not), vintage_access (a non-current vintage= on a plan without prior vintages), quarterly_access (a qtr on a plan without quarterly data), depth_cap_soc (a code deeper than the plan's depth cap), csv_access (a CSV or XLSX export on a plan without export, or a trial export outside the trial county); geography_excluded (strict=true and the entitlement excluded counties; excluded_fips, cap 50).

As application/problem+json.

X-DTG-Sandbox header
Present, with the value true, on every response to a sandbox API key, success and problem alike; it is set as soon as the credential is known. Every value in a sandbox response is synthetic and must never be published as data. Absent for live keys and sessions.

404 error

Not found. Codes: not_found (no such vintage in the caller's scope, or an X-DTG-Org header naming an organization the caller does not belong to), vintage_superseded (the pinned vintage was withdrawn and replaced by a correction; superseded_by names it; re-run against it), vintage_withdrawn (the pinned vintage was withdrawn and no correction exists yet; current names the vintage to use), region_retired (areas=region:<id> is retired: use cluster:<id>; replaced_by), cluster_not_found (a cluster:<id> that is not one of the organization's clusters of that kind), cluster_deleted (the cluster was deleted; cluster, restorable, restorable_until).

As application/problem+json.

X-DTG-Sandbox header
Present, with the value true, on every response to a sandbox API key, success and problem alike; it is set as soon as the credential is known. Every value in a sandbox response is synthetic and must never be published as data. Absent for live keys and sessions.

414 error

URI too long. Codes: uri_too_long (the request URL is longer than the configured maximum, 8,192 bytes by default).

As application/problem+json.

429 error

Too many requests. Codes: rate_limited (over the 120 requests a minute per credential or 300 a minute per organization; sandbox ceilings are lower; X-RateLimit-* say which), server_busy (the whole service is at capacity: every data slot and queue place is taken; retry_after; not metered, not a limit on the caller), sandbox_busy (sandbox keys only: the sandbox's shared concurrent-query maximum is reached; retry_after), points_exhausted (the organization's monthly points allowance is spent; Retry-After counts to the end of the UTC month; checked on API-key and export requests, never on session JSON), quota_exhausted (a monthly CSV or API request quota an operator has re-armed for this organization is spent; retired as a published limit). Every one carries Retry-After.

As application/problem+json.

Retry-After header
Seconds to wait before retrying.
X-RateLimit-Limit header
The per-minute request ceiling that applies.
X-RateLimit-Remaining header
Requests left in the current window.
X-RateLimit-Reset header
Seconds until the window refills: on every answer, the time until the bucket is full again; on a refusal, until a request is allowed (as Retry-After).
X-DTG-Sandbox header
Present, with the value true, on every response to a sandbox API key, success and problem alike; it is set as soon as the credential is known. Every value in a sandbox response is synthetic and must never be published as data. Absent for live keys and sessions.

500 error

Server error. Codes: internal_error (for example a vintage file the server cannot read).

As application/problem+json.

Problem codes: unknown_parameter (400), repeated_parameter (400), bad_request (400), xlsx_only_parameter (400), bad_cluster_ref (400), area_overlap (400), retired_msa (400), unknown_column (400), column_not_filterable (400), bad_total (400), xlsx_row_ceiling (400), xlsx_column_ceiling (400), unauthenticated (401), key_revoked (401), account_closed (401), org_closed (401), no_membership (403), account_suspended (403), org_suspended (403), org_closed (403), wrong_instance (403), plan_required (403), geography_excluded (403), not_found (404), vintage_superseded (404), vintage_withdrawn (404), region_retired (404), cluster_not_found (404), cluster_deleted (404), uri_too_long (414), rate_limited (429), server_busy (429), sandbox_busy (429), points_exhausted (429), quota_exhausted (429), internal_error (500). Every problem is a problem document.

GET /v2/occupation/lq

Occupation location quotients vs the nation

Credentials: API key or App session. Operation id dataOccupationLq.

Occupation location quotients versus the nation, computed in one query: for each code x area and period, (area code employment / area total) / (nation code employment / nation total), the total being the level-0 code. Employment only; there is no measure choice. Ownership is a filter applied to all four terms and is always summed (an explicit ownership list narrows it; sum=ownership is accepted and changes nothing). Codes, areas, summing, entitlement, the cell guard and paging work exactly as on /series; a summed area is the deduplicated union of its counties versus the nation. Points carry no labels: read meta.provenance_boundaries. Callers: an API key (live or sandbox) or a session, any member role. A live API key needs an organization whose plan includes the API add-on (403 plan_required, entitlement api_access) and that is not past due; a sandbox key needs neither and reads synthetic data. Gates, in order: authentication, the per-credential and per-organization rate ceilings, the global admission gate (429 server_busy; a refused request is not metered), then the one entitlement gate (API access, vintage access, quarterly access, export access and the monthly points allowance). Metering: a JSON request on a SESSION is never metered; a JSON request on an API key, and every CSV or XLSX export on any credential, spends points from the organization's monthly points allowance (the plan's figure, GET /v2/plans; a sandbox key spends a separate sandbox allowance). During a trial, an export whose geography lies wholly inside the trial county is free of the points allowance and of the export entitlement. Side effects: one usage record per request (with the points emitted, the vintage read and the counties read), the member's last-active time, and, when a key is refused for its rate or the points allowance, a notice to the key's holder and the organization's admins at most once a day. A session request records the person's presence (it cancels an open ownership claim on an organization they own). Representations (spec sections 10 and 10a): JSON by default. CSV with Accept: text/csv or a .csv suffix on the path (/v2/occupation/lq.csv); the suffix wins over Accept. The as-shown XLSX workbook with Accept: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet or an .xlsx suffix (/v2/occupation/lq.xlsx). CSV is long format, one line per series-point (soc, area, then the point's columns), headers exactly the JSON field names in JSON order, values exactly as JSON writes them (null as an empty field, booleans true/false), RFC 4180 quoting, CRLF, UTF-8 without a byte-order mark; a text cell a spreadsheet would read as a formula starts with '. A # comment preamble precedes the header: taigan-csv, endpoint, the sandbox warning (sandbox only), vintage, vintage_label, source (the attribution to keep), generated_at, granularity, provenance_boundaries, geography, the region lines (summed areas only), count, encoding. The workbook has a Table sheet (full-precision values under display number formats, codes stored as text) and a Source sheet (attribution, vintage, the shape and grid state in words). The as-shown parameters (shape, by_period, columns, sort, filters, computed, total) shape the workbook only and are 400 xlsx_only_parameter on JSON or CSV. Files are named <domain>-<endpoint>-<vintage>-<UTC timestamp>.csv (or .xlsx). Every problem, on any representation, is application/problem+json.

Parameters

socquery · string
Comma list of SOC codes (29-1141,29-1171), one series per code in the taxonomy's nested order; or cluster:<id>, one of the organization's soc code clusters, alone, summed into ONE series named with the cluster's name. With sum=codes any non-overlapping list sums into one series labelled sum(<n>); an ancestor and its descendant together are refused. Default (neither soc nor soc_level): the level-0 total (00-0000). Not with soc_level.
soc_levelquery · string
Every code at one hierarchy level (0 Total, 1 Major group, 2 Minor group, 3 Broad occupation, 4 Detailed occupation); or all, every code at every level in nested order (a browse: parents and children together, never summed); or leaf, the codes that are the parent of no code. all and leaf need the plan's full depth. Not with soc. (pattern ^(\d+|all|leaf)$)
areasquery · string
Comma list of area codes ("00" nation, "22" state, "22073" county), state:XX (expands to the state's counties, INCLUDING its "Unknown Or Undefined" catch-all — O34), cluster:<id> (the caller's own fips-kind cluster), or msa:<code> (the MSA's member counties at the latest map, never a catch-all). T19: a cluster or an MSA ALONE is one summed series named by the cluster's name or the MSA's title; BESIDE other tokens it contributes its counties to the one summed area and requires sum=areas (else 400 area_sum_required). A summed selection may not overlap: 400 area_overlap {pair}. A retired MSA is 400 retired_msa naming its successors. areas=region:<id> is RETIRED (404 region_retired, D44). The county universe is 3,194 areas: 3,144 counties and county-equivalents plus the 50 per-state catch-alls holding employment not attributable to a specific county — ordinary areas in the data, included in state and national totals, purchasable, but with no geographic extent (is_catchall on /v2/account/counties governs presentation only: never in an msa: expansion, never a /map row, never a public data page).
regionquery · stringdeprecated
RETIRED: any value is 404 region_retired. Use areas=cluster:<id>.
combinequery · "sum"deprecated
Deprecated alias of sum=areas. Only sum is defined; not with sum.
sumquery · string
Comma list of codes and areas to sum (as on /series). ownership is accepted and has no effect: LQ ownership is always summed.
ownershipquery · string
Comma list of fed, state, local, private (or 1, 2, 3, 5): the ownerships every term is summed over. Default all four. Never splits the answer by ownership.
granularityquery · "annual" | "quarterly"
annual (default) or quarterly (needs quarterly access). (default "annual")
fromquery · string
Start period, inclusive — "2004" (annual) or "2004q1" (quarterly); must match granularity=. (pattern ^\d{4}(q[1-4])?$)
toquery · string
End period, inclusive — same format and granularity as from=. (pattern ^\d{4}(q[1-4])?$)
strictquery · "true"
true: any excluded county makes the request 403 geography_excluded.
cursorquery · string
Paging: as on /series, a slice of the (code, area) list in response order; the cell guard measures the page. A page's values equal the same rows unpaged.
limitquery · integer
Paging: series per page, 1 to 5,000 (1,000 when only cursor is given). Absent with no cursor: unpaged. (default 1000; 1 to 5000)
vintagequery · string
The vintage to read (default: the current one). Pinning it makes the answer immutable and cacheable for a year. (pattern ^(\d{8}|sandbox-\d{4}\.\d+)$)
shapequery · "snapshot" | "by-period"
XLSX only. by-period: one row per series with a column per period per point field (emp_2019, ..., disclosure_2019, reliability_2019). Anything else: the snapshot, one row per series showing its last period.
by_periodquery · "1"
XLSX only. 1 is the same as shape=by-period.
columnsquery · string
XLSX only. The table's columns in order, comma-separated: response fields, title (the code's name), the computed columns, and on /series lq. Default: everything. On industry sheets disclosure and reliability (and lq_disclosure beside lq) are always inserted after the values they qualify, whatever is named; those forced labels are not metered. An unknown name is 400 unknown_column.
sortquery · string
XLSX only. <column>:<asc|desc> (default desc) on a snapshot value or a computed column; nulls last. (pattern ^[^:]+(:(asc|desc))?$)
filtersquery · string
XLSX only. Comma list of <column>:<ge|gt|le|lt>:<number>, or disclosure:published (keeps disclosure 0; in the by-period shape, every period exported must be published). A row with no value for a filtered column never passes; rank is not filterable.
computedquery · string
XLSX only. Comma list of emp_start (first period's employment), change_abs (last minus first), change_pct ((last minus first) / first x 100; null when first is 0), share (last / sum of every returned row's last x 100, taken before filtering), rank (1, 2, 2, 4 over the filtered rows, highest first, on the sort column or employment). Only emp_start is metered.
totalquery · string
XLSX only, /series: cluster:<id>, a code cluster of this domain whose summed series is appended beneath the rows as the table's total row (Total: <name>), not filtered, sorted or ranked; metered as its values are. On /lq a valid one is 400. (pattern ^cluster:[0-9a-fA-F-]{36}$)
X-DTG-Orgheader · string (uuid)
Session only: the organization the request acts in, when the person belongs to more than one (default: their own organization). An API key is bound to its organization; on a key this header must name that organization or be omitted. A malformed id, or an organization the caller is not an active member of, is 404 not_found.

Responses

200 success

The LQ series.

As application/json, text/csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.

X-RateLimit-Limit header
The per-minute request ceiling that applies.
X-RateLimit-Remaining header
Requests left in the current window.
X-RateLimit-Reset header
Seconds until the window refills: on every answer, the time until the bucket is full again; on a refusal, until a request is allowed (as Retry-After).
X-DTG-Sandbox header
Present, with the value true, on every response to a sandbox API key, success and problem alike; it is set as soon as the credential is known. Every value in a sandbox response is synthetic and must never be published as data. Absent for live keys and sessions.
Content-Disposition header
CSV and XLSX only: attachment; filename="<domain>-<endpoint>-<vintage>-<UTC timestamp>.csv" (or .xlsx), e.g. industry-series-20260913-20260927T184215Z.csv.
Cache-Control header
CSV and XLSX only: private, no-store.
Access-Control-Expose-Headers header
CSV and XLSX only: Content-Disposition, so a cross-origin first-party app can read the file name.
dataarray of DataLqSeriesOccupationalways present
The LQ series (this page of them, when paged), by code then area.
data[].socstringalways present
The SOC code as a string (29-1141), names on GET /v2/taxonomies/soc. On a summed code axis it is the label instead: the cluster's name for soc=cluster:<id>, or sum(<n>) for sum=codes over n codes.
data[].areastringalways present
The area: a FIPS code as a zero-padded string (00 the nation, 22 a state's own row, 22073 a county; a code ending 999 is that state's catch-all area, Unknown Or Undefined, holding employment QCEW cannot place in a county: a real, purchasable area included in state and national totals, with no geographic extent). On a summed-area series it is the label instead: the cluster's name or the MSA's title when one was asked for alone, otherwise custom region; meta.geography.region lists what was summed. The response carries codes, not area names: names are on GET /v2/taxonomies/areas.
data[].pointsarray of DataOccupationLqPointalways present
One point per period of the requested range, in period order, every period present (dense).
data[].points[].periodstringalways present
The period: 2021 or 2021q3, per granularity. (pattern ^\d{4}(q[1-4])?$)
data[].points[].lqnumber or nullalways present
The location quotient vs the nation, to 4 decimals; null on any zero denominator.
data[].points[].empnumberalways present
The area's own employment in the occupation for the period (the numerator).
metaDataMetaalways present
The meta of every v2 envelope (spec section 3). It is written after data on the wire. Clients must tolerate unknown fields.
meta.vintagestringalways present
The vintage the answer was read from: the internal build id (20260913; a sandbox key's are sandbox-2026.1). Cite it and pass it back as vintage= to reproduce the answer exactly: a released vintage never changes. Its public name, such as DTG 2026.3, is the label on GET /v2/vintages.
meta.sandboxtrue
Present, and true, only when the answer was read from a sandbox vintage: every value is synthetic and must never be published. Absent otherwise.
meta.generated_atstring (date-time)always present
When this response was produced, UTC, to the second (2026-09-27T18:42:15Z).
meta.granularity"annual" | "quarterly"
Data routes only: the period granularity of the points (on /map, quarterly when qtr was given). Absent on taxonomy responses.
meta.provenance_boundariesDataProvenanceBoundariesalways present
Where the vintage's published record ends and estimation begins, recorded per vintage at build time and returned on every envelope. Compare a point's period against these ranges to know what kind of value it is. For OCCUPATION data this is the only provenance there is: occupation values carry no per-value label, so a period inside actuals is published-basis, inside ces carried forward, inside projections projected. Industry values also carry their own disclosure code, which is authoritative for the value. The ranges move with each release; read them, never hard-code them.
meta.provenance_boundaries.actualsobjectalways present
Quarters published by BLS in QCEW (the Quarterly Census of Employment and Wages). Industry values here have disclosure 0 (published) or 1 (estimated by DTG where QCEW withheld the value).
meta.provenance_boundaries.actuals.fromstringalways present
First quarter of the range, lower case, e.g. 2004q1. (pattern ^\d{4}q[1-4]$)
meta.provenance_boundaries.actuals.tostringalways present
Last quarter of the range, lower case, e.g. 2025q4. (pattern ^\d{4}q[1-4]$)
meta.provenance_boundaries.cesobjectalways present
Quarters after the latest QCEW release, carried forward from the CES and SAE monthly employment surveys (their rate of change applied to DTG's base, not their levels). Industry values here have disclosure 2.
meta.provenance_boundaries.ces.fromstringalways present
First quarter of the range, lower case, e.g. 2004q1. (pattern ^\d{4}q[1-4]$)
meta.provenance_boundaries.ces.tostringalways present
Last quarter of the range, lower case, e.g. 2025q4. (pattern ^\d{4}q[1-4]$)
meta.provenance_boundaries.projectionsobjectalways present
Projected quarters, out to the end of the period axis. Industry values here have disclosure 3.
meta.provenance_boundaries.projections.fromstringalways present
First quarter of the range, lower case, e.g. 2004q1. (pattern ^\d{4}q[1-4]$)
meta.provenance_boundaries.projections.tostringalways present
Last quarter of the range, lower case, e.g. 2025q4. (pattern ^\d{4}q[1-4]$)
meta.geographyobject
Data routes only. Absent on taxonomy responses.
meta.geography.requestedintegeralways present
/series and /lq: the number of distinct areas the request resolved to before entitlement (a state:XX, msa: or cluster: token counts its counties; a plain state code counts 1; no areas counts the nation as 1). /map: the counties of the areas restriction, or without one the counties with a row for this code and period (catch-alls never counted). (at least 0)
meta.geography.returnedintegeralways present
Areas (on /map, counties) that passed entitlement and are in the answer. On a summed request these are the areas inside the one summed series. (at least 0)
meta.geography.excludedintegeralways present
Requested counties the organization's entitlement removed (outside the active county set, or every county while the subscription is past due). On /map a requested-versus-returned gap beyond excluded means counties with no data for the code and period, not an exclusion. (at least 0)
meta.geography.regionDataRegion
Present only on a request whose areas were summed into one series (a lone cluster:<id> or msa:<code>, or sum=areas / combine=sum): what the summed area is made of. The CSV preamble prints it as # region: lines and the workbook's Source sheet as Region rows.
meta.geography.region.labelstringalways present
The summed series' area label: the cluster's name or the MSA's title when one was asked for alone, otherwise custom region.
meta.geography.region.areasarray of DataRegionAreaalways present
Each area as requested, in request order.
meta.geography.region.areas[].tokenstringalways present
The token exactly as requested: 22073, 22, 00, state:22, msa:35380, cluster:<id>.
meta.geography.region.areas[].kind"nation" | "state" | "county" | "stateAll" | "msa" | "cluster"always present
What the token is: the nation (00), a state's own row (22), a county, stateAll (a state:XX token: every county of the state, its catch-all included), an MSA, or one of the organization's area clusters.
meta.geography.region.areas[].codestringalways present
The area's code: a FIPS code for a county or state (the 2-digit state code for stateAll), or the token itself, lower-cased, for msa:<code> and cluster:<id>.
meta.geography.region.areas[].namestring or nullalways present
The area's name: the vintage taxonomy's title for a county or state, the MSA's title, or the cluster's name. Null where the taxonomy has none.
meta.geography.region.areas[].resolvedarray of stringalways present
The area codes this token contributed to the sum after entitlement. A county the plan does not cover appears in no list; geography.excluded counts it.
meta.geography.region.resolvedarray of stringalways present
Every area code summed, deduplicated, in request order, after entitlement.
meta.paginationDataPaginationalways present
meta.pagination.cursorstring or nullalways present
The cursor this page was requested with; null for the first page or an unpaged request.
meta.pagination.next_cursorstring or nullalways present
Pass as cursor to get the next page. Null on the last page and on every unpaged response, so null always means there is nothing more.
meta.pagination.countintegeralways present
Rows in data on this response: series on /series and /lq, rows on /map, items on a taxonomy. (at least 0)

400 error

Bad request. Codes: unknown_parameter (a query parameter this route does not take; parameters[] names them), repeated_parameter (a parameter given more than once; parameter), bad_request (more than 50 query parameters); xlsx_only_parameter (an as-shown parameter on a JSON or CSV request; parameters[]); bad_request for a malformed vintage (a live key or a session takes YYYYMMDD, a sandbox key sandbox-YYYY.N; an id shaped for the other scope is refused the same way, so neither scope can probe the other); bad_request also for: a bad granularity; a from or to that is not YYYY or YYYYqN, a quarterly one without granularity=quarterly, one outside the vintage's period axis, or from after to; a bad cursor, a cursor past the end, or a limit outside 1 to 5,000; a bad sum token, or sum and combine together, or a combine other than sum; a malformed or unknown code, both soc= and soc_level=, a soc_level that is not an integer, all or leaf, a level with no codes, sum=codes with soc_level=all or leaf, an overlapping code set with sum=codes (the pair is named: "481 is contained in 48"), cluster:<id> not alone in soc=, or a malformed cluster reference; a bad ownership token; an unknown area, a malformed area code, a state:XX with no counties, a malformed msa: token, an unknown MSA, or a malformed cluster: token in areas; bad_cluster_ref (a cluster: id that is not a 36-character UUID); area_sum_required (an msa: or cluster: token beside other areas without sum=areas; token); area_overlap (a summed area list whose members overlap, D67; pair); retired_msa (an MSA with no counties in the current map; msa, retired_after, successors); cell_cap (the request, or the page, is more than 400,000 cells, a cell being one series at one period; cells, cap: narrow the areas, codes or period range, or page with cursor and limit); series_cap (more than the series cap per request, 10,000 by default; series, cap); XLSX only: unknown_column (a columns, sort or filters name the table does not have; column, parameter), column_not_filterable (a filter on rank), bad_total (a total that is not cluster:<uuid>), xlsx_row_ceiling (more rows than a sheet holds; rows, max_rows, alternatives), xlsx_column_ceiling (more columns than a sheet holds; columns, max_columns), bad_request for a malformed sort or filters entry or a total on /lq.

As application/problem+json.

X-DTG-Sandbox header
Present, with the value true, on every response to a sandbox API key, success and problem alike; it is set as soon as the credential is known. Every value in a sandbox response is synthetic and must never be published as data. Absent for live keys and sessions.

401 error

Unauthenticated. Codes: unauthenticated (no credential, or an API key or session that is unknown or expired); key_revoked (an API key that was revoked: revoked_at, and reason -- holder, admin, member_removed, seat_removed, api_disabled, addon_ended, account_closed, org_closed, retired, or null for a key revoked before T110 -- and a sentence saying when and why).

As application/problem+json.

403 error

Forbidden. Codes: no_membership (the account has no active organization membership), account_suspended (O56: the caller's own account is suspended by DTG; reason where recorded), org_suspended (the organization is suspended; organization {id, name} and, where recorded, reason), org_closed (the organization is closed; organization), wrong_instance (a key of the mode this process does not serve, for example a sandbox key on a live-only process; expected, and base_url naming the address that serves it); plan_required with entitlement naming what the plan lacks: api_access (a live API key on an organization without the API add-on), past_due (the organization's subscription is past due: every request on a live API key, every CSV or XLSX export, and county detail are paused; nation and state data on a session are not), vintage_access (a non-current vintage= on a plan without prior vintages), quarterly_access (granularity=quarterly on a plan without quarterly data), csv_access (a CSV or XLSX export on a plan without export, or a trial export outside the trial county), depth_cap_soc (a code deeper than the plan's soc depth cap; soc_level=all and leaf need the full depth); geography_excluded (every requested county is outside the organization's entitlement, or any is with strict=true; excluded_fips lists up to 50).

As application/problem+json.

X-DTG-Sandbox header
Present, with the value true, on every response to a sandbox API key, success and problem alike; it is set as soon as the credential is known. Every value in a sandbox response is synthetic and must never be published as data. Absent for live keys and sessions.

404 error

Not found. Codes: not_found (no such vintage in the caller's scope, or an X-DTG-Org header naming an organization the caller does not belong to), vintage_superseded (the pinned vintage was withdrawn and replaced by a correction; superseded_by names it; re-run against it), vintage_withdrawn (the pinned vintage was withdrawn and no correction exists yet; current names the vintage to use), region_retired (areas=region:<id> or the region parameter is retired: use cluster:<id>; replaced_by), cluster_not_found (a cluster:<id> that is not one of the organization's clusters of that kind), cluster_deleted (the cluster was deleted; cluster, restorable, restorable_until).

As application/problem+json.

X-DTG-Sandbox header
Present, with the value true, on every response to a sandbox API key, success and problem alike; it is set as soon as the credential is known. Every value in a sandbox response is synthetic and must never be published as data. Absent for live keys and sessions.

414 error

URI too long. Codes: uri_too_long (the request URL is longer than the configured maximum, 8,192 bytes by default).

As application/problem+json.

429 error

Too many requests. Codes: rate_limited (over the 120 requests a minute per credential or 300 a minute per organization; sandbox ceilings are lower; X-RateLimit-* say which), server_busy (the whole service is at capacity: every data slot and queue place is taken; retry_after; not metered, not a limit on the caller), sandbox_busy (sandbox keys only: the sandbox's shared concurrent-query maximum is reached; retry_after), points_exhausted (the organization's monthly points allowance is spent; Retry-After counts to the end of the UTC month; checked on API-key and export requests, never on session JSON), quota_exhausted (a monthly CSV or API request quota an operator has re-armed for this organization is spent; retired as a published limit). Every one carries Retry-After.

As application/problem+json.

Retry-After header
Seconds to wait before retrying.
X-RateLimit-Limit header
The per-minute request ceiling that applies.
X-RateLimit-Remaining header
Requests left in the current window.
X-RateLimit-Reset header
Seconds until the window refills: on every answer, the time until the bucket is full again; on a refusal, until a request is allowed (as Retry-After).
X-DTG-Sandbox header
Present, with the value true, on every response to a sandbox API key, success and problem alike; it is set as soon as the credential is known. Every value in a sandbox response is synthetic and must never be published as data. Absent for live keys and sessions.

500 error

Server error. Codes: internal_error (for example a vintage file the server cannot read).

As application/problem+json.

Problem codes: unknown_parameter (400), repeated_parameter (400), bad_request (400), xlsx_only_parameter (400), bad_cluster_ref (400), area_sum_required (400), area_overlap (400), retired_msa (400), cell_cap (400), series_cap (400), unknown_column (400), column_not_filterable (400), bad_total (400), xlsx_row_ceiling (400), xlsx_column_ceiling (400), unauthenticated (401), key_revoked (401), account_closed (401), org_closed (401), no_membership (403), account_suspended (403), org_suspended (403), org_closed (403), wrong_instance (403), plan_required (403), geography_excluded (403), not_found (404), vintage_superseded (404), vintage_withdrawn (404), region_retired (404), cluster_not_found (404), cluster_deleted (404), uri_too_long (414), rate_limited (429), server_busy (429), sandbox_busy (429), points_exhausted (429), quota_exhausted (429), internal_error (500). Every problem is a problem document.

GET /v2/occupation/staffing-pattern

Staffing pattern of an industry (occupations, shares and job counts)

Credentials: API key or App session. Operation id dataOccupationStaffingPattern.

The occupations employed in one industry, in one or more areas and one year: each occupation's share of the industry's jobs and its job count. The pattern is the regionalised OEWS staffing pattern of DTG's occupation pipeline; the vintage carries the 832 leaf occupations (518 detailed, 314 broad where a broad occupation does not split), which sum to one; soc_level sums them up the SOC tree (levels 0 to 3 sum to one; level 4 holds only the detailed leaves, about 45% of an industry). industry= takes a public NAICS code only: a code finer than the pattern's industry inherits its pattern (industry_resolution names the code it took), a broader one blends the patterns beneath it by DTG employment. Areas, entitlement and gates as on /v2/occupation/series (no new gate, D99 item 8): nation and state patterns are open to every account; a county needs the county in the organization's active county set (a partial exclusion is counted in meta.geography.excluded; strict=true makes it a 403). A state or the nation asked for directly serves its own staffing pattern (M5: it reproduces /v2/occupation/series exactly); any other area (a county, an MSA, a cluster, a summed list) is the blend of the staffing regions its counties lie in, weighted by DTG industry employment for the area, industry and year; meta.staffing.patterns[].regions names the regions, their weights and OEWS windows. Ownership: default all, the blend of the four ownership patterns weighted by the area's employment in each (no all-ownership pattern exists, M2); one ownership serves its own pattern; a list gives one pattern per ownership, or with sum=ownership their blend. Annual only, one year (default: the last year of published actuals). COUNTS: Industry-by-occupation job counts are DTG modelled estimates: the occupation's share of the industry, from the regionalised OEWS staffing pattern, applied to DTG's industry employment. They are not survey counts. A count is the share x the requested industry's own annual employment exactly as /v2/industry/series serves it, so an industry's counts sum to that figure within the rounding of each count to the cent. The complete pattern is served: unlike /v2/occupation/series, no occupation is dropped under a floor (Q5). An industry with employment in the area and year but no staffing pattern is a defect in the data release (the owner's rule, enforced at release by gate G1): the answer is 500 pattern_missing, logged, never a partial pattern. A national fill-in (the pipeline copied the nation's pattern where the region had no survey data, M6) is served and labelled (basis, note, national_fill_in). Metering: a JSON request on a SESSION is never metered; on an API key, and every CSV or XLSX export, each value that shows non-zero at its display precision is one point (a share to 4 decimal places, a count to whole jobs); labels cost nothing. Representations: JSON by default; CSV with Accept: text/csv or a .csv suffix (long format: the row's fields as columns, values as JSON writes them; a # preamble with the vintage, the attribution, the year, the counts' sentence, the floor note and one # pattern: line per area and ownership); the workbook with the spreadsheet media type or an .xlsx suffix (a Table sheet with the code's title beside it, shares under a percentage format and counts under a whole-jobs format, and a Source sheet). The as-shown grid parameters of /series are not taken. Callers: an API key (live or sandbox) or a session, any member role; a live key needs the API add-on (403 plan_required, api_access).

Parameters

industryquery · stringrequired
One public NAICS code (622, 622110, 62), as on /v2/industry/series. The pattern's internal industry vocabulary (EP) is never accepted or shown. (pattern ^\d+$)
soc_levelquery · string
leaf (the default): the 832 leaf occupations, each at its most detailed code, which partition the industry. An integer: the leaves summed to that level of the SOC tree (0 Total, 1 Major group, 2 Minor group, 3 Broad, 4 Detailed: partial, a broad leaf has no detailed code). all: every code with leaves beneath it, nested, parents beside their children (never to be summed). leaf and all need the plan's full soc depth. (pattern ^(\d+|all|leaf)$)
areasquery · string
Comma list of area codes ("00" nation, "22" state, "22073" county), state:XX (expands to the state's counties, INCLUDING its "Unknown Or Undefined" catch-all — O34), cluster:<id> (the caller's own fips-kind cluster), or msa:<code> (the MSA's member counties at the latest map, never a catch-all). T19: a cluster or an MSA ALONE is one summed series named by the cluster's name or the MSA's title; BESIDE other tokens it contributes its counties to the one summed area and requires sum=areas (else 400 area_sum_required). A summed selection may not overlap: 400 area_overlap {pair}. A retired MSA is 400 retired_msa naming its successors. areas=region:<id> is RETIRED (404 region_retired, D44). The county universe is 3,194 areas: 3,144 counties and county-equivalents plus the 50 per-state catch-alls holding employment not attributable to a specific county — ordinary areas in the data, included in state and national totals, purchasable, but with no geographic extent (is_catchall on /v2/account/counties governs presentation only: never in an msa: expansion, never a /map row, never a public data page).
sumquery · string
Comma list of areas (sum the areas list into one area, whose pattern is the blend of its staffing regions; D67's overlap rule applies) and ownership (blend an explicit ownership= list into one pattern). codes is refused: a staffing pattern's rows are already parts of one whole.
ownershipquery · string
Comma list of fed, state, local, private (or 1, 2, 3, 5). Default: all, the blend of the four ownership patterns weighted by the area's employment in each (there is no all-ownership pattern, M2). A list gives one pattern per ownership; with sum=ownership their blend, labelled with the subset.
yearquery · string
The year, YYYY: the pattern that year and the industry's annual employment (the average of its four quarters) for the counts. Default: the last year of published actuals (meta.provenance_boundaries.actuals.to). Projection years are served (the pattern's last epoch runs to the end of the axis); quarters are not. (pattern ^\d{4}$)
strictquery · "true"
true: any county the entitlement excludes makes the request 403 geography_excluded instead of a 200 with meta.geography.excluded above zero.
vintagequery · string
The vintage to read (default: the current one). A vintage built before the staffing patterns were added answers 404 staffing_not_in_vintage. (pattern ^(\d{8}|sandbox-\d{4}\.\d+)$)
X-DTG-Orgheader · string (uuid)
Session only: the organization the request acts in, when the person belongs to more than one. An API key is bound to its organization.

Responses

200 success

The staffing pattern.

As application/json, text/csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.

X-RateLimit-Limit header
The per-minute request ceiling that applies.
X-RateLimit-Remaining header
Requests left in the current window.
X-RateLimit-Reset header
Seconds until the window refills: on every answer, the time until the bucket is full again; on a refusal, until a request is allowed (as Retry-After).
X-DTG-Sandbox header
Present, with the value true, on every response to a sandbox API key, success and problem alike; it is set as soon as the credential is known. Every value in a sandbox response is synthetic and must never be published as data. Absent for live keys and sessions.
Content-Disposition header
CSV and XLSX only: attachment; filename="<domain>-staffing-pattern-<vintage>-<UTC timestamp>.csv" (or .xlsx).
Cache-Control header
CSV and XLSX only: private, no-store.
Access-Control-Expose-Headers header
CSV and XLSX only: Content-Disposition.
dataarray of DataOccupationStaffingRowalways present
The rows.
data[].socstringalways present
The SOC code (29-1141).
data[].areastringalways present
The area, as on /v2/occupation/series (a FIPS code, or a summed area's label).
data[].ownershipstringalways present
all, one of fed, state, local, private, or a summed subset.
data[].sharenumberalways present
The occupation's share of the industry's jobs, a fraction (the leaves sum to 1), written to 10 decimal places. Shown and metered to 4 places (a percentage to 2): below 0.00005 it shows as 0 and costs no point.
data[].countnumberalways present
The jobs: share x the industry's employment (industry_employment), to the cent, shown and metered as whole jobs (below 0.5 it costs no point). Industry-by-occupation job counts are DTG modelled estimates: the occupation's share of the industry, from the regionalised OEWS staffing pattern, applied to DTG's industry employment. They are not survey counts.
metaDataStaffingEnvelopeMetaalways present
The envelope's meta (spec section 3) with staffing beside it.
meta.vintagestringalways present
The vintage the answer was read from: the internal build id (20260913; a sandbox key's are sandbox-2026.1). Cite it and pass it back as vintage= to reproduce the answer exactly: a released vintage never changes. Its public name, such as DTG 2026.3, is the label on GET /v2/vintages.
meta.sandboxtrue
Present, and true, only when the answer was read from a sandbox vintage: every value is synthetic and must never be published. Absent otherwise.
meta.generated_atstring (date-time)always present
When this response was produced, UTC, to the second (2026-09-27T18:42:15Z).
meta.granularity"annual" | "quarterly"
Data routes only: the period granularity of the points (on /map, quarterly when qtr was given). Absent on taxonomy responses.
meta.provenance_boundariesDataProvenanceBoundariesalways present
Where the vintage's published record ends and estimation begins, recorded per vintage at build time and returned on every envelope. Compare a point's period against these ranges to know what kind of value it is. For OCCUPATION data this is the only provenance there is: occupation values carry no per-value label, so a period inside actuals is published-basis, inside ces carried forward, inside projections projected. Industry values also carry their own disclosure code, which is authoritative for the value. The ranges move with each release; read them, never hard-code them.
meta.provenance_boundaries.actualsobjectalways present
Quarters published by BLS in QCEW (the Quarterly Census of Employment and Wages). Industry values here have disclosure 0 (published) or 1 (estimated by DTG where QCEW withheld the value).
meta.provenance_boundaries.actuals.fromstringalways present
First quarter of the range, lower case, e.g. 2004q1. (pattern ^\d{4}q[1-4]$)
meta.provenance_boundaries.actuals.tostringalways present
Last quarter of the range, lower case, e.g. 2025q4. (pattern ^\d{4}q[1-4]$)
meta.provenance_boundaries.cesobjectalways present
Quarters after the latest QCEW release, carried forward from the CES and SAE monthly employment surveys (their rate of change applied to DTG's base, not their levels). Industry values here have disclosure 2.
meta.provenance_boundaries.ces.fromstringalways present
First quarter of the range, lower case, e.g. 2004q1. (pattern ^\d{4}q[1-4]$)
meta.provenance_boundaries.ces.tostringalways present
Last quarter of the range, lower case, e.g. 2025q4. (pattern ^\d{4}q[1-4]$)
meta.provenance_boundaries.projectionsobjectalways present
Projected quarters, out to the end of the period axis. Industry values here have disclosure 3.
meta.provenance_boundaries.projections.fromstringalways present
First quarter of the range, lower case, e.g. 2004q1. (pattern ^\d{4}q[1-4]$)
meta.provenance_boundaries.projections.tostringalways present
Last quarter of the range, lower case, e.g. 2025q4. (pattern ^\d{4}q[1-4]$)
meta.geographyobject
Data routes only. Absent on taxonomy responses.
meta.geography.requestedintegeralways present
/series and /lq: the number of distinct areas the request resolved to before entitlement (a state:XX, msa: or cluster: token counts its counties; a plain state code counts 1; no areas counts the nation as 1). /map: the counties of the areas restriction, or without one the counties with a row for this code and period (catch-alls never counted). (at least 0)
meta.geography.returnedintegeralways present
Areas (on /map, counties) that passed entitlement and are in the answer. On a summed request these are the areas inside the one summed series. (at least 0)
meta.geography.excludedintegeralways present
Requested counties the organization's entitlement removed (outside the active county set, or every county while the subscription is past due). On /map a requested-versus-returned gap beyond excluded means counties with no data for the code and period, not an exclusion. (at least 0)
meta.geography.regionDataRegion
Present only on a request whose areas were summed into one series (a lone cluster:<id> or msa:<code>, or sum=areas / combine=sum): what the summed area is made of. The CSV preamble prints it as # region: lines and the workbook's Source sheet as Region rows.
meta.geography.region.labelstringalways present
The summed series' area label: the cluster's name or the MSA's title when one was asked for alone, otherwise custom region.
meta.geography.region.areasarray of DataRegionAreaalways present
Each area as requested, in request order.
meta.geography.region.areas[].tokenstringalways present
The token exactly as requested: 22073, 22, 00, state:22, msa:35380, cluster:<id>.
meta.geography.region.areas[].kind"nation" | "state" | "county" | "stateAll" | "msa" | "cluster"always present
What the token is: the nation (00), a state's own row (22), a county, stateAll (a state:XX token: every county of the state, its catch-all included), an MSA, or one of the organization's area clusters.
meta.geography.region.areas[].codestringalways present
The area's code: a FIPS code for a county or state (the 2-digit state code for stateAll), or the token itself, lower-cased, for msa:<code> and cluster:<id>.
meta.geography.region.areas[].namestring or nullalways present
The area's name: the vintage taxonomy's title for a county or state, the MSA's title, or the cluster's name. Null where the taxonomy has none.
meta.geography.region.areas[].resolvedarray of stringalways present
The area codes this token contributed to the sum after entitlement. A county the plan does not cover appears in no list; geography.excluded counts it.
meta.geography.region.resolvedarray of stringalways present
Every area code summed, deduplicated, in request order, after entitlement.
meta.paginationDataPaginationalways present
meta.pagination.cursorstring or nullalways present
The cursor this page was requested with; null for the first page or an unpaged request.
meta.pagination.next_cursorstring or nullalways present
Pass as cursor to get the next page. Null on the last page and on every unpaged response, so null always means there is nothing more.
meta.pagination.countintegeralways present
Rows in data on this response: series on /series and /lq, rows on /map, items on a taxonomy. (at least 0)
meta.staffingDataStaffingMeta
meta.staffing: what the pattern is and where it came from.
meta.staffing.yearintegeralways present
The year served.
meta.staffing.industryobject
Forward endpoint: the industry asked for.
meta.staffing.industry.naicsstring
The NAICS code.
meta.staffing.industry.titlestring or null
Its title.
meta.staffing.occupationobject
Reverse endpoint: the occupation asked for.
meta.staffing.occupation.socstring
The SOC code.
meta.staffing.occupation.titlestring or null
Its title.
meta.staffing.occupation.leavesinteger
The leaf occupations its figures sum.
meta.staffing.industriesobject
Reverse endpoint: the list, as naics_level or naics.
meta.staffing.industries.naics_levelstring
The level listed.
meta.staffing.industries.naicsarray of string
The codes listed.
meta.staffing.soc_levelstring
Forward endpoint: leaf, all or the level.
meta.staffing.ownership_blendstring
Present when a pattern blends ownerships: how (there is no all-ownership pattern).
meta.staffing.ep_base_yearinteger or null
The base year of the Employment Projections crosswalk the patterns were built with (inferred at build, M4).
meta.staffing.weights_sourcestring
What the blends and the counts are weighted by.
meta.staffing.countsstringalways present
The counts' standing, in the owner's words: Industry-by-occupation job counts are DTG modelled estimates: the occupation's share of the industry, from the regionalised OEWS staffing pattern, applied to DTG's industry employment. They are not survey counts.
meta.staffing.floorstringalways present
The complete pattern is served; /v2/occupation/series applies a floor the pattern does not (Q5).
meta.staffing.share_display_decimalsinteger
The places a share is shown and metered at, as a fraction (4: a percentage to 2 places). A share that shows as 0 costs no point. The wire carries 10 places.
meta.staffing.share_of_occupationstring
Reverse endpoint: what share_of_occupation is a share of.
meta.staffing.patternsarray of DataStaffingPatternalways present
One entry per area x ownership of the answer.
meta.staffing.patterns[].areastringalways present
The area, as the rows carry it.
meta.staffing.patterns[].ownershipstringalways present
The ownership, as the rows carry it (all, one token, or a summed subset).
meta.staffing.patterns[].industry_employmentnumber
Forward endpoint: the industry's employment in the area and year, the annual average to the cent, exactly as /v2/industry/series serves it. The rows' counts sum to it within rounding.
meta.staffing.patterns[].basis"regional" | "national" | "partly national"
Forward endpoint: regional, or national when the whole pattern is a national fill-in, or partly national when part of the industry's employment stands behind one.
meta.staffing.patterns[].notestring
Present when basis is not regional: pattern: national; no regional survey data for this industry in this area, or the partly-national form with the share of the industry's employment.
meta.staffing.patterns[].industry_resolutionarray of object
Forward endpoint: how the NAICS code reached a pattern, per ownership with employment: exact; inherited with inherited_from (the NAICS code whose pattern a finer code took); blended with blended_from (the NAICS codes beneath it whose patterns were blended by employment).
meta.staffing.patterns[].industry_resolution[].ownershipstring
The ownership.
meta.staffing.patterns[].industry_resolution[].resolution"exact" | "inherited" | "blended"
How the code reached its pattern.
meta.staffing.patterns[].industry_resolution[].inherited_fromstring
The NAICS code whose pattern was taken.
meta.staffing.patterns[].industry_resolution[].blended_fromarray of string
The NAICS codes whose patterns were blended.
meta.staffing.patterns[].occupation_employmentnumber
Reverse endpoint: the occupation's jobs summed over the industries listed (the denominator of share_of_occupation).
meta.staffing.patterns[].industriesinteger
Reverse endpoint: the rows this area and ownership produced.
meta.staffing.patterns[].inheritedarray of object
Reverse endpoint: the listed industries that took an ancestor's pattern.
meta.staffing.patterns[].inherited[].naicsstring
The listed code.
meta.staffing.patterns[].inherited[].ownershipstring
The ownership.
meta.staffing.patterns[].inherited[].inherited_fromstring
The code whose pattern it took.
meta.staffing.patterns[].nationalarray of string
Reverse endpoint: the listed industries whose pattern is wholly or partly a national fill-in.
meta.staffing.patterns[].regionsarray of DataStaffingRegionalways present
The staffing regions the pattern was read from.
meta.staffing.patterns[].regions[].regionstringalways present
The staffing region's code in the source's numbering: 99 the nation, a two-digit code a state, a five-digit code a metropolitan area, a seven-digit code a state's nonmetropolitan area.
meta.staffing.patterns[].regions[].titlestring or nullalways present
The region's name.
meta.staffing.patterns[].regions[].type"nation" | "state" | "metro" | "nonmetro" | nullalways present
What kind of region it is.
meta.staffing.patterns[].regions[].ownershipstringalways present
The ownership (fed, state, local, private) whose pattern this entry weighs.
meta.staffing.patterns[].regions[].weightnumber
Forward endpoint only: the jobs standing behind this region in this ownership (DTG industry employment, the year's annual average).
meta.staffing.patterns[].regions[].weight_sharenumber or null
Forward endpoint only: this entry's part of the pattern's weight, 6 decimal places.
meta.staffing.patterns[].regions[].oews_windowstring or nullalways present
The OEWS survey years the pattern was fitted to (2023-2025), derived by the pipeline's own rule; several, comma-separated, when the region's industries differ.
meta.staffing.patterns[].regions[].national_fill_inbooleanalways present
True when the pattern (or part of it) is a national fill-in: the pipeline copied the nation's pattern because the region had no survey data for the industry (M6).

400 error

Bad request. Codes: unknown_parameter (parameters[]), repeated_parameter (parameter), bad_request for: a missing industry, a code that is not digits (an EP code is refused as unknown), an unknown NAICS code; a bad or empty soc_level; a malformed vintage; a bad ownership token; a sum token other than areas or ownership (sum=codes does not apply); a year that is not YYYY or lies outside the vintage's staffing patterns; an unknown or malformed area, state:XX, msa: or cluster: token; bad_cluster_ref; area_sum_required (token); area_overlap (pair); retired_msa; cell_cap (cells, cap) or series_cap (series, cap): the request asks for more rows (areas x ownerships x rows each) than the one guard allows; XLSX only: xlsx_row_ceiling (rows, max_rows, alternatives).

As application/problem+json.

X-DTG-Sandbox header
Present, with the value true, on every response to a sandbox API key, success and problem alike; it is set as soon as the credential is known. Every value in a sandbox response is synthetic and must never be published as data. Absent for live keys and sessions.

401 error

Unauthenticated. Codes: unauthenticated (no credential, or an unknown or expired one); key_revoked (revoked_at, reason).

As application/problem+json.

403 error

Forbidden. Codes: no_membership, account_suspended, org_suspended, org_closed, wrong_instance; plan_required with entitlement: api_access, past_due, vintage_access, csv_access (a CSV or XLSX export on a plan without export, or a trial export outside the trial county), depth_cap_naics or depth_cap_soc (a code or level deeper than the plan's cap); geography_excluded (every requested county is outside the organization's entitlement, or any is with strict=true; excluded_fips).

As application/problem+json.

X-DTG-Sandbox header
Present, with the value true, on every response to a sandbox API key, success and problem alike; it is set as soon as the credential is known. Every value in a sandbox response is synthetic and must never be published as data. Absent for live keys and sessions.

404 error

Not found. Codes: not_found (no such vintage in the caller's scope, or an X-DTG-Org organization the caller does not belong to), vintage_superseded (superseded_by), vintage_withdrawn (current), staffing_not_in_vintage (the vintage was built before the staffing patterns were added: vintage), region_retired, cluster_not_found, cluster_deleted.

As application/problem+json.

X-DTG-Sandbox header
Present, with the value true, on every response to a sandbox API key, success and problem alike; it is set as soon as the credential is known. Every value in a sandbox response is synthetic and must never be published as data. Absent for live keys and sessions.

414 error

URI too long. Codes: uri_too_long.

As application/problem+json.

429 error

Too many requests. Codes: rate_limited, server_busy (retry_after; not metered), sandbox_busy (retry_after), points_exhausted (the organization's monthly points allowance is spent; checked on API-key and export requests, never on session JSON), quota_exhausted. Every one carries Retry-After.

As application/problem+json.

Retry-After header
Seconds to wait before retrying.
X-RateLimit-Limit header
The per-minute request ceiling that applies.
X-RateLimit-Remaining header
Requests left in the current window.
X-RateLimit-Reset header
Seconds until the window refills: on every answer, the time until the bucket is full again; on a refusal, until a request is allowed (as Retry-After).
X-DTG-Sandbox header
Present, with the value true, on every response to a sandbox API key, success and problem alike; it is set as soon as the credential is known. Every value in a sandbox response is synthetic and must never be published as data. Absent for live keys and sessions.

500 error

Server error. Codes: pattern_missing (an industry with employment in the area and year has no staffing pattern in this vintage: a defect in the data release, logged; no partial pattern is served; vintage), internal_error.

As application/problem+json.

Problem codes: unknown_parameter (400), repeated_parameter (400), bad_request (400), bad_cluster_ref (400), area_sum_required (400), area_overlap (400), retired_msa (400), cell_cap (400), series_cap (400), xlsx_row_ceiling (400), unauthenticated (401), key_revoked (401), no_membership (403), account_suspended (403), org_suspended (403), org_closed (403), wrong_instance (403), plan_required (403), geography_excluded (403), not_found (404), vintage_superseded (404), vintage_withdrawn (404), staffing_not_in_vintage (404), region_retired (404), cluster_not_found (404), cluster_deleted (404), uri_too_long (414), rate_limited (429), server_busy (429), sandbox_busy (429), points_exhausted (429), quota_exhausted (429), pattern_missing (500), internal_error (500). Every problem is a problem document.

Generated from taigan/openapi.yaml at catahoula 77925fab712f(API document version 2.0).