Industry data
4 endpoints, generated from the API's own OpenAPI document.
GET /v2/industry/series
Industry series
Credentials: API key or App session. Operation id dataIndustrySeries.
Industry time series (QCEW 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), disclosure takes the maximum, reliability is employment-weighted, the restated flags OR, avg_pay is recomputed from the summed numerator and denominator, quarters_present takes the minimum over members with data. 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/industry/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/industry/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, disclosure and reliability (the key to the labels), 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, and the key to disclosure and reliability). 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
naicsquery · string- Comma list of NAICS platform codes (
481,482), one series per code in the taxonomy's nested order; orcluster:<id>, one of the organization's naics code clusters, alone, summed into ONE series named with the cluster's name. Withsum=codesany non-overlapping list sums into one series labelledsum(<n>); an ancestor and its descendant together are refused. Default (neithernaicsnornaics_level): the level-0 total (10). Not withnaics_level. naics_levelquery · string- Every code at one hierarchy level (0 Total, 1 Domain, 2 Super-sector, 3 Sector, 4 to 7 the 3- to 6-digit codes); or
all, every code at every level in nested order (a browse: parents and children together, never summed); orleaf, the codes that are the parent of no code.allandleafneed the plan's full depth. Not withnaics. (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. Useareas=cluster:<id>. combinequery · "sum"deprecated- Deprecated alias of
sum=areas: sums theareaslist into one series. Onlysumis defined; not withsum. sumquery · string- Uniform summing (D37-FINAL): a comma-separated list of the axes
to collapse —
codes,areas, and (v0.11)ownership, which sums an explicitownership=subset into ONE series labelled with the subset (e.g.local,private); withorder=ownershipit 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 withsum=ownershipone series summed over the subset. granularityquery · "annual" | "quarterly"annual(default) orquarterly. Quarterly needs the plan's quarterly access (403plan_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,estabs,earnings,avg_pay(default all). Labels are always returned on industry points. orderquery · "naics" | "area" | "ownership"- The major dimension of series ORDER: a sort key only, never a grouping; the code axis is never collapsed.
ownershipalso splits the default summed ownership into one series per ownership. Not withgroup. (default "naics") groupquery · "naics" | "area" | "ownership"deprecated- Deprecated alias of
order; removal would be a new major version, announced first. Not withorder. strictquery · "true"true: any county the entitlement excludes makes the request 403geography_excluded(listing up to 50) instead of a 200 withmeta.geography.excludedabove zero; while the organization is past due, 403plan_requiredwithpast_dueinstead.cursorquery · string- Paging: the opaque
meta.pagination.next_cursorof the previous page. A page is a slice of the series list in the order the response emits; the cell guard measures the page. Givingcursororlimitturns 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
cursoris given). Absent with nocursor: 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.
1is the same asshape=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 /serieslq. Default: everything. On industry sheetsdisclosureandreliability(andlq_disclosurebesidelq) are always inserted after the values they qualify, whatever is named; those forced labels are not metered. An unknown name is 400unknown_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>, ordisclosure: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;rankis 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). Onlyemp_startis 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-Limitheader- The per-minute request ceiling that applies.
X-RateLimit-Remainingheader- Requests left in the current window.
X-RateLimit-Resetheader- 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-Sandboxheader- 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-Dispositionheader- CSV and XLSX only:
attachment; filename="<domain>-<endpoint>-<vintage>-<UTC timestamp>.csv"(or.xlsx), e.g.industry-series-20260913-20260927T184215Z.csv. Cache-Controlheader- CSV and XLSX only:
private, no-store. Access-Control-Expose-Headersheader- CSV and XLSX only:
Content-Disposition, so a cross-origin first-party app can read the file name.
dataarray of DataSeriesIndustryalways present- The series (this page of them, when paged), in the order
ordersets. Empty only when a page or selection holds none. data[].naicsstringalways present- The NAICS platform code as a string (
481111), names onGET /v2/taxonomies/naics. On a summed code axis it is the label instead: the cluster's name fornaics=cluster:<id>, orsum(<n>)forsum=codesover n codes. data[].areastringalways present- The area: a FIPS code as a zero-padded string (
00the nation,22a state's own row,22073a county; a code ending999is 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, otherwisecustom region;meta.geography.regionlists what was summed. The response carries codes, not area names: names are onGET /v2/taxonomies/areas. data[].ownershipstringalways present- The ownership the series covers:
all(the default: federal, state and local government and private summed), one offed,state,local,private(an unsummed series, from an explicitownership=list ororder=ownership), or withsum=ownershipthe summed subset as tokens in code order, e.g.local,private. data[].pointsarray of DataIndustryPointalways present- One point per period of the requested range, in period order, every period present (dense).
data[].points[].periodstringalways present- The period:
2021(annual) or2021q3(quarterly), pergranularity. (pattern ^\d{4}(q[1-4])?$) data[].points[].empnumber- Average employment, in jobs covered by state unemployment-insurance programs: jobs, not people (a person with two covered jobs counts twice). A quarter is its three-month average; an annual value is the sum of its four quarters divided by 4. Present when
measuresincludes emp (the default). data[].points[].estabsnumber- Establishments: single economic units at one location, not firms (one firm may run many). Averaged over a year like emp (sum of quarters divided by 4), so an annual value can be fractional. QCEW publishes establishment counts even where employment is withheld, so establishments beside a small estimated employment mean real but limited activity. Present when
measuresincludes estabs. data[].points[].earningsnumber- Total wages paid in the period as reported for unemployment-insurance purposes (employee contributions and withheld taxes included; employer contributions excluded), in US dollars. An annual value is the SUM of its four quarters. Present when
measuresincludes earnings. data[].points[].avg_paynumber or null- Pay per JOB for the period in US dollars (not an annual salary, not an hourly rate): earnings divided by employment, from the summed numerator and denominator and rounded once. Annual: the year's earnings divided by its average employment, which is QCEW's average annual pay. Null when employment is zero. Present when
measuresincludes avg_pay. data[].points[].disclosure0 | 1 | 2 | 3always present- Industry only. Where this value came from: 0 = published by BLS in QCEW, as published; 1 = estimated by DTG where QCEW withheld (suppressed) the value to protect an employer's identity; 2 = carried forward past the latest QCEW release using the CES and SAE monthly surveys (their rate of change applied to DTG's base); 3 = projected. Suppression is a property of the QCEW source, not a judgement about the area. A summed value (a code set, a summed area, the default sum across ownerships, and on an annual point the year's four quarters) carries the HIGHEST code among the values summed, so an annual point whose year mixes published and projected quarters reports 3. A period with no source row is a true zero (sparse means zero) and reports 0.
data[].points[].reliabilitynumber or nullalways present- Industry only. 0 to 1, two decimals: how tightly the published QCEW record constrains this value. Published values (disclosure 0) score 1; an estimated value (disclosure 1) scores lower the wider the range of values the surrounding published totals leave open. Null where no score is computed at all: the value is carried forward or projected (disclosure 2 or 3, including an annual point whose year contains such a quarter), or there is no employment. A null is not a zero. A summed value's score is the employment-weighted mean of its parts, so it describes the total rather than its weakest member and a summed series can read 1.00 while carrying disclosure 1: the disclosure code is the authoritative published-or-estimated signal. Not a ranking of areas, not a confidence interval and not a margin of error. (0 to 1)
data[].points[].naics_restatedbooleanalways present- Industry only. True where this value changed when the full series was restated onto this vintage's NAICS industry classification. Applied per value: a parent code is flagged only where its own value changed. Not a quality warning: it explains why a DTG figure can differ from what BLS published for that year at the time. A summed value is true if any part is. False on a period with no source row.
data[].points[].fips_restatedbooleanalways present- Industry only. As naics_restated, for county-definition (FIPS) restatement: true where this value changed when the series was restated onto the current county boundaries. A summed value is true if any part is.
data[].points[].quarters_presentinteger- Annual points only (absent on quarterly ones): how many of the year's quarters have a source row behind the annual figure; 4 is a complete year and 0 means the year is a true zero. On a summed series, the minimum over the summed members that have at least one source quarter in the year (a member with none is a true zero, not a partial year). (0 to 4)
metaDataMetaalways present- The
metaof every v2 envelope (spec section 3). It is written afterdataon 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 aresandbox-2026.1). Cite it and pass it back asvintage=to reproduce the answer exactly: a released vintage never changes. Its public name, such asDTG 2026.3, is thelabelonGET /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,
quarterlywhenqtrwas 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
actualsis published-basis, insidecescarried forward, insideprojectionsprojected. Industry values also carry their owndisclosurecode, 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:orcluster:token counts its counties; a plain state code counts 1; noareascounts the nation as 1). /map: the counties of theareasrestriction, 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
excludedmeans 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>ormsa:<code>, orsum=areas/combine=sum): what the summed area is made of. The CSV preamble prints it as# region:lines and the workbook's Source sheet asRegionrows. meta.geography.region.labelstringalways present- The summed series'
arealabel: the cluster's name or the MSA's title when one was asked for alone, otherwisecustom 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(astate:XXtoken: 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, formsa:<code>andcluster:<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.excludedcounts it. meta.geography.region.resolvedarray of stringalways present- Every area code summed, deduplicated, in request order, after entitlement.
meta.paginationDataPaginationalways presentmeta.pagination.cursorstring or nullalways present- The
cursorthis page was requested with; null for the first page or an unpaged request. meta.pagination.next_cursorstring or nullalways present- Pass as
cursorto 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
dataon 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 naics= and naics_level=, a naics_level that is not an integer, all or leaf, a level with no codes, sum=codes with naics_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 naics=, 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-Sandboxheader- 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_naics (a code deeper than the plan's naics depth cap; naics_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-Sandboxheader- 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-Sandboxheader- 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-Afterheader- Seconds to wait before retrying.
X-RateLimit-Limitheader- The per-minute request ceiling that applies.
X-RateLimit-Remainingheader- Requests left in the current window.
X-RateLimit-Resetheader- 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-Sandboxheader- 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/industry/map
Industry choropleth values (annual or quarterly)
Credentials: API key or App session. Operation id dataIndustryMap.
Choropleth values for one NAICS platform 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. Each row carries disclosure and reliability for its value. 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/industry/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/industry/map.xlsx). CSV is long format, one line per row (fips, value, disclosure, reliability), 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, disclosure and reliability (the key to the labels), 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, and the key to disclosure and reliability). 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
naicsquery · stringrequired- One NAICS platform code; or
cluster:<id>, one of the organization's naics code clusters, summed per area; or withsum=codesa comma list of non-overlapping codes summed per area. measurequery · "emp" | "estabs" | "earnings" | "avg_pay"- 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
areastokens (counties, a state code orstate:XXfor 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 (400area_overlap).00alone means no restriction (the whole map); beside other tokens it overlaps them.region:<id>is 404region_retired. sumquery · stringcodes: sum the comma list innaicsinto one value per area.areasis a 400 (a map is per area);ownershipis accepted and has no effect (ownership is always summed here).combinequery · "sum"deprecated- Accepted for symmetry with /series;
combine=sumis always a 400 here (a map is per area). strictquery · "true"true: a county the organization's entitlement excludes is a 403geography_excludedlisting the excluded FIPS (cap 50), or the past-due refusal when the pause is the cause; without it they are left out and counted inmeta.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.
1is the same asshape=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 /serieslq. Default: everything. On industry sheetsdisclosureandreliability(andlq_disclosurebesidelq) are always inserted after the values they qualify, whatever is named; those forced labels are not metered. An unknown name is 400unknown_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>, ordisclosure: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;rankis 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). Onlyemp_startis 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 400bad_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-Limitheader- The per-minute request ceiling that applies.
X-RateLimit-Remainingheader- Requests left in the current window.
X-RateLimit-Resetheader- 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-Sandboxheader- 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-Dispositionheader- CSV and XLSX only:
attachment; filename="<domain>-<endpoint>-<vintage>-<UTC timestamp>.csv"(or.xlsx), e.g.industry-series-20260913-20260927T184215Z.csv. Cache-Controlheader- CSV and XLSX only:
private, no-store. Access-Control-Expose-Headersheader- CSV and XLSX only:
Content-Disposition, so a cross-origin first-party app can read the file name.
dataarray of DataMapRowalways 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
measurefor the area, summed across the ownership filter and anysum=codescode set: emp and estabs average the four quarters on an annual map (sum divided by 4); earnings sums them; avg_pay is earnings over average employment, rounded once. Null for avg_pay when employment is zero. data[].disclosure0 | 1 | 2 | 3always present- The highest disclosure code among the cells summed into the value (the quarters of an annual map, the ownerships, a code set), by DataIndustryPoint.disclosure's meaning.
data[].reliabilitynumber or nullalways present- Employment-weighted reliability of the value, by DataIndustryPoint.reliability's rules; null when the value is carried forward or projected or there is no employment. (0 to 1)
metaDataMetaalways present- The
metaof every v2 envelope (spec section 3). It is written afterdataon 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 aresandbox-2026.1). Cite it and pass it back asvintage=to reproduce the answer exactly: a released vintage never changes. Its public name, such asDTG 2026.3, is thelabelonGET /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,
quarterlywhenqtrwas 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
actualsis published-basis, insidecescarried forward, insideprojectionsprojected. Industry values also carry their owndisclosurecode, 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:orcluster:token counts its counties; a plain state code counts 1; noareascounts the nation as 1). /map: the counties of theareasrestriction, 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
excludedmeans 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>ormsa:<code>, orsum=areas/combine=sum): what the summed area is made of. The CSV preamble prints it as# region:lines and the workbook's Source sheet asRegionrows. meta.geography.region.labelstringalways present- The summed series'
arealabel: the cluster's name or the MSA's title when one was asked for alone, otherwisecustom 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(astate:XXtoken: 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, formsa:<code>andcluster:<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.excludedcounts it. meta.geography.region.resolvedarray of stringalways present- Every area code summed, deduplicated, in request order, after entitlement.
meta.paginationDataPaginationalways presentmeta.pagination.cursorstring or nullalways present- The
cursorthis page was requested with; null for the first page or an unpaged request. meta.pagination.next_cursorstring or nullalways present- Pass as
cursorto 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
dataon 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 naics, 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-Sandboxheader- 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_naics (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-Sandboxheader- 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-Sandboxheader- 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-Afterheader- Seconds to wait before retrying.
X-RateLimit-Limitheader- The per-minute request ceiling that applies.
X-RateLimit-Remainingheader- Requests left in the current window.
X-RateLimit-Resetheader- 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-Sandboxheader- 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/industry/lq
Industry location quotients vs the nation
Credentials: API key or App session. Operation id dataIndustryLq.
Industry 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. Each point carries the area cell's own disclosure and reliability and the quotient's lq_disclosure. 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/industry/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/industry/lq.xlsx). CSV is long format, one line per series-point (naics, 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, disclosure and reliability (the key to the labels), lq_disclosure, 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, and the key to disclosure and reliability). 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
naicsquery · string- Comma list of NAICS platform codes (
481,482), one series per code in the taxonomy's nested order; orcluster:<id>, one of the organization's naics code clusters, alone, summed into ONE series named with the cluster's name. Withsum=codesany non-overlapping list sums into one series labelledsum(<n>); an ancestor and its descendant together are refused. Default (neithernaicsnornaics_level): the level-0 total (10). Not withnaics_level. naics_levelquery · string- Every code at one hierarchy level (0 Total, 1 Domain, 2 Super-sector, 3 Sector, 4 to 7 the 3- to 6-digit codes); or
all, every code at every level in nested order (a browse: parents and children together, never summed); orleaf, the codes that are the parent of no code.allandleafneed the plan's full depth. Not withnaics. (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. Useareas=cluster:<id>. combinequery · "sum"deprecated- Deprecated alias of
sum=areas. Onlysumis defined; not withsum. sumquery · string- Comma list of
codesandareasto sum (as on /series).ownershipis 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) orquarterly(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 403geography_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
cursoris given). Absent with nocursor: 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.
1is the same asshape=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 /serieslq. Default: everything. On industry sheetsdisclosureandreliability(andlq_disclosurebesidelq) are always inserted after the values they qualify, whatever is named; those forced labels are not metered. An unknown name is 400unknown_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>, ordisclosure: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;rankis 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). Onlyemp_startis 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-Limitheader- The per-minute request ceiling that applies.
X-RateLimit-Remainingheader- Requests left in the current window.
X-RateLimit-Resetheader- 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-Sandboxheader- 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-Dispositionheader- CSV and XLSX only:
attachment; filename="<domain>-<endpoint>-<vintage>-<UTC timestamp>.csv"(or.xlsx), e.g.industry-series-20260913-20260927T184215Z.csv. Cache-Controlheader- CSV and XLSX only:
private, no-store. Access-Control-Expose-Headersheader- CSV and XLSX only:
Content-Disposition, so a cross-origin first-party app can read the file name.
dataarray of DataLqSeriesIndustryalways present- The LQ series (this page of them, when paged), by code then area.
data[].naicsstringalways present- The NAICS platform code as a string (
481111), names onGET /v2/taxonomies/naics. On a summed code axis it is the label instead: the cluster's name fornaics=cluster:<id>, orsum(<n>)forsum=codesover n codes. data[].areastringalways present- The area: a FIPS code as a zero-padded string (
00the nation,22a state's own row,22073a county; a code ending999is 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, otherwisecustom region;meta.geography.regionlists what was summed. The response carries codes, not area names: names are onGET /v2/taxonomies/areas. data[].pointsarray of DataLqPointalways present- One point per period of the requested range, in period order, every period present (dense).
data[].points[].periodstringalways present- The period:
2021or2021q3, pergranularity. (pattern ^\d{4}(q[1-4])?$) data[].points[].lqnumber or nullalways present- The location quotient, to 4 decimals (shown to 2). Above 1: the area is more concentrated in this code than the nation; below 1, less. Null when any denominator is zero (the area's total, the nation's code or the nation's total employment).
data[].points[].empnumberalways present- The area's own employment in the code for the period (the LQ's numerator), on /series' basis (annual = sum of quarters divided by 4).
data[].points[].disclosure0 | 1 | 2 | 3always present- The disclosure code of the area's own cell, the
empthis point carries, exactly as /series labels that cell (see DataIndustryPoint.disclosure). data[].points[].reliabilitynumber or nullalways present- The reliability of the area's own cell, by DataIndustryPoint.reliability's rules (null when carried forward, projected, or no employment). No reliability is given for the ratio itself. (0 to 1)
data[].points[].lq_disclosure0 | 1 | 2 | 3always present- The HIGHEST disclosure code among the four values the quotient is computed from (the area and the nation, in the code and in the total): an LQ is as estimated as its most estimated input. Use this, not
disclosure, to qualifylq. metaDataMetaalways present- The
metaof every v2 envelope (spec section 3). It is written afterdataon 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 aresandbox-2026.1). Cite it and pass it back asvintage=to reproduce the answer exactly: a released vintage never changes. Its public name, such asDTG 2026.3, is thelabelonGET /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,
quarterlywhenqtrwas 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
actualsis published-basis, insidecescarried forward, insideprojectionsprojected. Industry values also carry their owndisclosurecode, 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:orcluster:token counts its counties; a plain state code counts 1; noareascounts the nation as 1). /map: the counties of theareasrestriction, 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
excludedmeans 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>ormsa:<code>, orsum=areas/combine=sum): what the summed area is made of. The CSV preamble prints it as# region:lines and the workbook's Source sheet asRegionrows. meta.geography.region.labelstringalways present- The summed series'
arealabel: the cluster's name or the MSA's title when one was asked for alone, otherwisecustom 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(astate:XXtoken: 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, formsa:<code>andcluster:<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.excludedcounts it. meta.geography.region.resolvedarray of stringalways present- Every area code summed, deduplicated, in request order, after entitlement.
meta.paginationDataPaginationalways presentmeta.pagination.cursorstring or nullalways present- The
cursorthis page was requested with; null for the first page or an unpaged request. meta.pagination.next_cursorstring or nullalways present- Pass as
cursorto 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
dataon 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 naics= and naics_level=, a naics_level that is not an integer, all or leaf, a level with no codes, sum=codes with naics_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 naics=, 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-Sandboxheader- 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_naics (a code deeper than the plan's naics depth cap; naics_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-Sandboxheader- 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-Sandboxheader- 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-Afterheader- Seconds to wait before retrying.
X-RateLimit-Limitheader- The per-minute request ceiling that applies.
X-RateLimit-Remainingheader- Requests left in the current window.
X-RateLimit-Resetheader- 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-Sandboxheader- 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/industry/staffing-pattern
Industries employing an occupation (both shares and job counts)
Credentials: API key or App session. Operation id dataIndustryStaffingPattern.
The industries that employ one occupation, in one or more areas and one year (D99 Q3): for each industry, share_of_industry (the occupation's share of the industry's jobs, from the industry's staffing pattern), the job count (that share x the industry's employment), and share_of_occupation (the industry's count over the sum of the counts of the industries listed for the same area and ownership). An industry in which the occupation has no jobs is not listed. occupation= takes any SOC code; a code above the leaves sums the leaf occupations beneath it. The industries listed are one NAICS level (naics_level, default 4, the three-digit subsector) or a naics= list; they must not overlap, since an ancestor beside its descendant would count the occupation's jobs twice. Each row's pattern_basis says whether its pattern is regional or a national fill-in. 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
occupationquery · stringrequired- One SOC code (
29-1141,29-1140,29-0000). Above the leaf level its shares and counts are the sums of the leaf occupations beneath it (meta.staffing.occupation.leavescounts them). naics_levelquery · string- The industries listed: every NAICS code at one level (default
4, the three-digit subsector), orleaf, the codes that are the parent of no code.allis refused: parents beside their children would count jobs twice. Not withnaics. (pattern ^(\d+|leaf)$) naicsquery · string- The industries listed instead of a level: a comma list of public NAICS codes, none containing another (an ancestor and its descendant together are 400, the pair named). Not with
naics_level. 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 theareaslist into one area, whose pattern is the blend of its staffing regions; D67's overlap rule applies) andownership(blend an explicitownership=list into one pattern).codesis 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; withsum=ownershiptheir 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 403geography_excludedinstead of a 200 withmeta.geography.excludedabove 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-Limitheader- The per-minute request ceiling that applies.
X-RateLimit-Remainingheader- Requests left in the current window.
X-RateLimit-Resetheader- 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-Sandboxheader- 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-Dispositionheader- CSV and XLSX only:
attachment; filename="<domain>-staffing-pattern-<vintage>-<UTC timestamp>.csv"(or.xlsx). Cache-Controlheader- CSV and XLSX only:
private, no-store. Access-Control-Expose-Headersheader- CSV and XLSX only:
Content-Disposition.
dataarray of DataIndustryStaffingRowalways present- The rows.
data[].naicsstringalways present- The NAICS code.
data[].areastringalways present- The area, as on
/v2/industry/series. data[].ownershipstringalways presentall, one ownership, or a summed subset.data[].share_of_industrynumberalways present- The occupation's share of this industry's jobs (10 places on the wire, shown and metered to 4).
data[].share_of_occupationnumber or nullalways present- This industry's part of the occupation's jobs across the industries listed for the same area and ownership (sums to 1 over them).
data[].countnumberalways present- The occupation's jobs in the industry, to the cent, shown and metered as whole jobs. 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.
data[].pattern_basis"regional" | "national" | "partly national"always present- A label (free): whether the industry's pattern here is regional or a national fill-in (M6).
metaDataStaffingEnvelopeMetaalways present- The envelope's
meta(spec section 3) withstaffingbeside it. meta.vintagestringalways present- The vintage the answer was read from: the internal build id (
20260913; a sandbox key's aresandbox-2026.1). Cite it and pass it back asvintage=to reproduce the answer exactly: a released vintage never changes. Its public name, such asDTG 2026.3, is thelabelonGET /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,
quarterlywhenqtrwas 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
actualsis published-basis, insidecescarried forward, insideprojectionsprojected. Industry values also carry their owndisclosurecode, 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:orcluster:token counts its counties; a plain state code counts 1; noareascounts the nation as 1). /map: the counties of theareasrestriction, 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
excludedmeans 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>ormsa:<code>, orsum=areas/combine=sum): what the summed area is made of. The CSV preamble prints it as# region:lines and the workbook's Source sheet asRegionrows. meta.geography.region.labelstringalways present- The summed series'
arealabel: the cluster's name or the MSA's title when one was asked for alone, otherwisecustom 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(astate:XXtoken: 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, formsa:<code>andcluster:<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.excludedcounts it. meta.geography.region.resolvedarray of stringalways present- Every area code summed, deduplicated, in request order, after entitlement.
meta.paginationDataPaginationalways presentmeta.pagination.cursorstring or nullalways present- The
cursorthis page was requested with; null for the first page or an unpaged request. meta.pagination.next_cursorstring or nullalways present- Pass as
cursorto 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
dataon this response: series on /series and /lq, rows on /map, items on a taxonomy. (at least 0) meta.staffingDataStaffingMetameta.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_levelornaics. meta.staffing.industries.naics_levelstring- The level listed.
meta.staffing.industries.naicsarray of string- The codes listed.
meta.staffing.soc_levelstring- Forward endpoint:
leaf,allor 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/seriesapplies 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_occupationis 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/seriesserves it. The rows' counts sum to it within rounding. meta.staffing.patterns[].basis"regional" | "national" | "partly national"- Forward endpoint:
regional, ornationalwhen the whole pattern is a national fill-in, orpartly nationalwhen part of the industry's employment stands behind one. meta.staffing.patterns[].notestring- Present when
basisis notregional: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;inheritedwithinherited_from(the NAICS code whose pattern a finer code took);blendedwithblended_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:
99the 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 or unknown occupation; naics and naics_level together; a malformed or unknown naics code; overlapping naics codes (the pair named); naics_level=all; a naics_level with no codes; 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-Sandboxheader- 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-Sandboxheader- 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-Sandboxheader- 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.
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-Afterheader- Seconds to wait before retrying.
X-RateLimit-Limitheader- The per-minute request ceiling that applies.
X-RateLimit-Remainingheader- Requests left in the current window.
X-RateLimit-Resetheader- 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-Sandboxheader- 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).