---
title: "Taxonomies endpoints — API reference | Data Tactical Group"
route: "/docs/api/reference/taxonomies"
updated: "2026-10-23"
---

# Taxonomies

9 endpoints, generated from the API's own OpenAPI document. The codes the data routes take: NAICS, SOC, areas, MSAs, ownership, periods. Not metered; cacheable.

## GET `/v2/taxonomies/naics/levels`

**The NAICS hierarchy's level names**

Credentials: API key or App session. Operation id `dataListNaicsLevels`.

The names behind `level` on NAICS items and the `naics` hierarchy (0 Total, 1 Domain, 2 Super-sector, 3 Sector, 4 to 7 the 3- to 6-digit codes), from the vintage's level lookup. Never paged. Any authenticated caller: an API key (live or sandbox) or a session, any role. Taxonomies are free: not behind the entitlement gate, not admission-gated, not metered, and not limited by plan (a non-current `vintage=` needs no prior-vintage entitlement here; a withdrawn or superseded one is still refused). A sandbox key reads the sandbox vintage's taxonomy, which is real apart from its five invented counties and MSAs. Caching: with an explicit `vintage=` the answer can never change and is `Cache-Control: public, max-age=31536000, immutable`; without it, `no-cache` with the same strong `ETag`, so revalidating with `If-None-Match` answers an empty 304 until a release moves the current vintage. Problems are never cached.

### Parameters

**`vintage` query · 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+)$)

**`X-DTG-Org` header · 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`](/docs/api/errors#code-not-found).

### Responses

#### `200` success

The levels, in level order.

As `application/json`.

**`X-RateLimit-Limit` header**

The per-minute request ceiling that applies.

**`X-RateLimit-Remaining` header**

Requests left in the current window.

**`X-RateLimit-Reset` header**

Seconds until the window refills: on every answer, the time until the bucket is full again; on a refusal, until a request is allowed (as `Retry-After`).

**`X-DTG-Sandbox` header**

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

**`ETag` header**

A strong tag over the body less `meta.generated_at`: the same for a pinned and an unpinned read of the same content and for every credential.

**`Cache-Control` header**

`public, max-age=31536000, immutable` when `vintage=` was given; `no-cache` otherwise.

**`data` array of DataLevelItem always present**

Every level, in level order.

**`data[].level` integer always present**

The level number used by `level` on taxonomy items and `naics_level` / `soc_level` on the data routes. (at least 0)

**`data[].name` string always present**

The level's name, e.g. `Sector` (NAICS 3), `Detailed occupation` (SOC 4), `County` (areas 2).

**`meta` DataMeta always present**

The `meta` of every v2 envelope (spec section 3). It is written after `data` on the wire. Clients must tolerate unknown fields.

**`meta.vintage` string always present**

The vintage the answer was read from: the internal build id (`20260913`; a sandbox key's are `sandbox-2026.1`). Cite it and pass it back as `vintage=` to reproduce the answer exactly: a released vintage never changes. Its public name, such as `DTG 2026.3`, is the `label` on `GET /v2/vintages`.

**`meta.sandbox` true**

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_at` string (date-time) always present**

When this response was produced, UTC, to the second (`2026-09-27T18:42:15Z`).

**`meta.granularity` "annual" | "quarterly"**

Data routes only: the period granularity of the points (on /map, `quarterly` when `qtr` was given). Absent on taxonomy responses.

**`meta.provenance_boundaries` DataProvenanceBoundaries always present**

Where the vintage's published record ends and estimation begins, recorded per vintage at build time and returned on every envelope. Compare a point's period against these ranges to know what kind of value it is. For OCCUPATION data this is the only provenance there is: occupation values carry no per-value label, so a period inside `actuals` is published-basis, inside `ces` carried forward, inside `projections` projected. Industry values also carry their own `disclosure` code, which is authoritative for the value. The ranges move with each release; read them, never hard-code them.

**`meta.provenance_boundaries.actuals` object always 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.from` string always present**

First quarter of the range, lower case, e.g. `2004q1`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.actuals.to` string always present**

Last quarter of the range, lower case, e.g. `2025q4`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.ces` object always 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.from` string always present**

First quarter of the range, lower case, e.g. `2004q1`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.ces.to` string always present**

Last quarter of the range, lower case, e.g. `2025q4`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.projections` object always present**

Projected quarters, out to the end of the period axis. Industry values here have disclosure 3.

**`meta.provenance_boundaries.projections.from` string always present**

First quarter of the range, lower case, e.g. `2004q1`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.projections.to` string always present**

Last quarter of the range, lower case, e.g. `2025q4`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.geography` object**

Data routes only. Absent on taxonomy responses.

**`meta.geography.requested` integer always present**

/series and /lq: the number of distinct areas the request resolved to before entitlement (a `state:XX`, `msa:` or `cluster:` token counts its counties; a plain state code counts 1; no `areas` counts the nation as 1). /map: the counties of the `areas` restriction, or without one the counties with a row for this code and period (catch-alls never counted). (at least 0)

**`meta.geography.returned` integer always 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.excluded` integer always present**

Requested counties the organization's entitlement removed (outside the active county set, or every county while the subscription is past due). On /map a requested-versus-returned gap beyond `excluded` means counties with no data for the code and period, not an exclusion. (at least 0)

**`meta.geography.region` DataRegion**

Present only on a request whose areas were summed into one series (a lone `cluster:<id>` or `msa:<code>`, or `sum=areas` / `combine=sum`): what the summed area is made of. The CSV preamble prints it as `# region:` lines and the workbook's Source sheet as `Region` rows.

**`meta.geography.region.label` string always present**

The summed series' `area` label: the cluster's name or the MSA's title when one was asked for alone, otherwise `custom region`.

**`meta.geography.region.areas` array of DataRegionArea always present**

Each area as requested, in request order.

**`meta.geography.region.areas[].token` string always present**

The token exactly as requested: `22073`, `22`, `00`, `state:22`, `msa:35380`, `cluster:<id>`.

**`meta.geography.region.areas[].kind` "nation" | "state" | "county" | "stateAll" | "msa" | "cluster" always present**

What the token is: the nation (`00`), a state's own row (`22`), a county, `stateAll` (a `state:XX` token: every county of the state, its catch-all included), an MSA, or one of the organization's area clusters.

**`meta.geography.region.areas[].code` string always present**

The area's code: a FIPS code for a county or state (the 2-digit state code for `stateAll`), or the token itself, lower-cased, for `msa:<code>` and `cluster:<id>`.

**`meta.geography.region.areas[].name` string or null always 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[].resolved` array of string always present**

The area codes this token contributed to the sum after entitlement. A county the plan does not cover appears in no list; `geography.excluded` counts it.

**`meta.geography.region.resolved` array of string always present**

Every area code summed, deduplicated, in request order, after entitlement.

**`meta.pagination` DataPagination always present**

**`meta.pagination.cursor` string or null always present**

The `cursor` this page was requested with; null for the first page or an unpaged request.

**`meta.pagination.next_cursor` string or null always present**

Pass as `cursor` to get the next page. Null on the last page and on every unpaged response, so null always means there is nothing more.

**`meta.pagination.count` integer always present**

Rows in `data` on this response: series on /series and /lq, rows on /map, items on a taxonomy. (at least 0)

#### `304` redirect

Not modified: the `If-None-Match` tag still matches. No body.

**`ETag` header**

The current tag.

#### `400` error

Bad request. Codes: [`unknown_parameter`](/docs/api/errors#code-unknown-parameter) (a query parameter this route does not take; `parameters[]` names them), [`repeated_parameter`](/docs/api/errors#code-repeated-parameter) (a parameter given more than once; `parameter`), [`bad_request`](/docs/api/errors#code-bad-request) (more than 50 query parameters), [`bad_request`](/docs/api/errors#code-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).

As `application/problem+json`.

#### `401` error

Unauthenticated. Codes: [`unauthenticated`](/docs/api/errors#code-unauthenticated) (no credential, or an API key or session that is unknown or expired); [`key_revoked`](/docs/api/errors#code-key-revoked) (an API key that was revoked: `revoked_at`, and `reason` -- `holder`, `admin`, `member_removed`, `seat_removed`, `api_disabled`, `addon_ended`, [`account_closed`](/docs/api/errors#code-account-closed), [`org_closed`](/docs/api/errors#code-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`](/docs/api/errors#code-no-membership) (the account has no active organization membership), [`account_suspended`](/docs/api/errors#code-account-suspended) (O56: the caller's own account is suspended by DTG; `reason` where recorded), [`org_suspended`](/docs/api/errors#code-org-suspended) (the organization is suspended; `organization` {id, name} and, where recorded, `reason`), [`org_closed`](/docs/api/errors#code-org-closed) (the organization is closed; `organization`), [`wrong_instance`](/docs/api/errors#code-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).

As `application/problem+json`.

#### `404` error

Not found. Codes: [`not_found`](/docs/api/errors#code-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`](/docs/api/errors#code-vintage-superseded) (the pinned vintage was withdrawn and replaced by a correction; `superseded_by` names it; re-run against it), [`vintage_withdrawn`](/docs/api/errors#code-vintage-withdrawn) (the pinned vintage was withdrawn and no correction exists yet; `current` names the vintage to use).

As `application/problem+json`.

#### `414` error

URI too long. Codes: [`uri_too_long`](/docs/api/errors#code-uri-too-long) (the request URL is longer than the configured maximum, 8,192 bytes by default).

As `application/problem+json`.

#### `429` error

Rate limited, and only that: [`rate_limited`](/docs/api/errors#code-rate-limited) (over the 120 requests a minute per credential or 300 a minute per organization; the sandbox ceilings are lower). `Retry-After` and `X-RateLimit-*` say when to retry. This route is not admission-gated or metered.

As `application/problem+json`.

**`Retry-After` header**

Seconds to wait before retrying.

**`X-RateLimit-Limit` header**

The per-minute request ceiling that applies.

**`X-RateLimit-Remaining` header**

Requests left in the current window.

**`X-RateLimit-Reset` header**

Seconds until the window refills: on every answer, the time until the bucket is full again; on a refusal, until a request is allowed (as `Retry-After`).

#### `500` error

Server error. Codes: [`internal_error`](/docs/api/errors#code-internal-error) (for example a vintage file the server cannot read).

As `application/problem+json`.

Problem codes: [`unknown_parameter`](/docs/api/errors#code-unknown-parameter) (400), [`repeated_parameter`](/docs/api/errors#code-repeated-parameter) (400), [`bad_request`](/docs/api/errors#code-bad-request) (400), [`unauthenticated`](/docs/api/errors#code-unauthenticated) (401), [`key_revoked`](/docs/api/errors#code-key-revoked) (401), [`account_closed`](/docs/api/errors#code-account-closed) (401), [`org_closed`](/docs/api/errors#code-org-closed) (401), [`no_membership`](/docs/api/errors#code-no-membership) (403), [`account_suspended`](/docs/api/errors#code-account-suspended) (403), [`org_suspended`](/docs/api/errors#code-org-suspended) (403), [`org_closed`](/docs/api/errors#code-org-closed) (403), [`wrong_instance`](/docs/api/errors#code-wrong-instance) (403), [`not_found`](/docs/api/errors#code-not-found) (404), [`vintage_superseded`](/docs/api/errors#code-vintage-superseded) (404), [`vintage_withdrawn`](/docs/api/errors#code-vintage-withdrawn) (404), [`uri_too_long`](/docs/api/errors#code-uri-too-long) (414), [`rate_limited`](/docs/api/errors#code-rate-limited) (429), [`internal_error`](/docs/api/errors#code-internal-error) (500). Every problem is a [problem document](/docs/api/errors).

## GET `/v2/taxonomies/soc/levels`

**The SOC hierarchy's level names**

Credentials: API key or App session. Operation id `dataListSocLevels`.

The names behind `level` on SOC items and the `soc` hierarchy (0 Total, 1 Major group, 2 Minor group, 3 Broad occupation, 4 Detailed occupation), from the vintage's level lookup. Never paged. Any authenticated caller: an API key (live or sandbox) or a session, any role. Taxonomies are free: not behind the entitlement gate, not admission-gated, not metered, and not limited by plan (a non-current `vintage=` needs no prior-vintage entitlement here; a withdrawn or superseded one is still refused). A sandbox key reads the sandbox vintage's taxonomy, which is real apart from its five invented counties and MSAs. Caching: with an explicit `vintage=` the answer can never change and is `Cache-Control: public, max-age=31536000, immutable`; without it, `no-cache` with the same strong `ETag`, so revalidating with `If-None-Match` answers an empty 304 until a release moves the current vintage. Problems are never cached.

### Parameters

**`vintage` query · 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+)$)

**`X-DTG-Org` header · 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`](/docs/api/errors#code-not-found).

### Responses

#### `200` success

The levels, in level order.

As `application/json`.

**`X-RateLimit-Limit` header**

The per-minute request ceiling that applies.

**`X-RateLimit-Remaining` header**

Requests left in the current window.

**`X-RateLimit-Reset` header**

Seconds until the window refills: on every answer, the time until the bucket is full again; on a refusal, until a request is allowed (as `Retry-After`).

**`X-DTG-Sandbox` header**

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

**`ETag` header**

A strong tag over the body less `meta.generated_at`: the same for a pinned and an unpinned read of the same content and for every credential.

**`Cache-Control` header**

`public, max-age=31536000, immutable` when `vintage=` was given; `no-cache` otherwise.

**`data` array of DataLevelItem always present**

Every level, in level order.

**`data[].level` integer always present**

The level number used by `level` on taxonomy items and `naics_level` / `soc_level` on the data routes. (at least 0)

**`data[].name` string always present**

The level's name, e.g. `Sector` (NAICS 3), `Detailed occupation` (SOC 4), `County` (areas 2).

**`meta` DataMeta always present**

The `meta` of every v2 envelope (spec section 3). It is written after `data` on the wire. Clients must tolerate unknown fields.

**`meta.vintage` string always present**

The vintage the answer was read from: the internal build id (`20260913`; a sandbox key's are `sandbox-2026.1`). Cite it and pass it back as `vintage=` to reproduce the answer exactly: a released vintage never changes. Its public name, such as `DTG 2026.3`, is the `label` on `GET /v2/vintages`.

**`meta.sandbox` true**

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_at` string (date-time) always present**

When this response was produced, UTC, to the second (`2026-09-27T18:42:15Z`).

**`meta.granularity` "annual" | "quarterly"**

Data routes only: the period granularity of the points (on /map, `quarterly` when `qtr` was given). Absent on taxonomy responses.

**`meta.provenance_boundaries` DataProvenanceBoundaries always present**

Where the vintage's published record ends and estimation begins, recorded per vintage at build time and returned on every envelope. Compare a point's period against these ranges to know what kind of value it is. For OCCUPATION data this is the only provenance there is: occupation values carry no per-value label, so a period inside `actuals` is published-basis, inside `ces` carried forward, inside `projections` projected. Industry values also carry their own `disclosure` code, which is authoritative for the value. The ranges move with each release; read them, never hard-code them.

**`meta.provenance_boundaries.actuals` object always 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.from` string always present**

First quarter of the range, lower case, e.g. `2004q1`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.actuals.to` string always present**

Last quarter of the range, lower case, e.g. `2025q4`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.ces` object always 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.from` string always present**

First quarter of the range, lower case, e.g. `2004q1`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.ces.to` string always present**

Last quarter of the range, lower case, e.g. `2025q4`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.projections` object always present**

Projected quarters, out to the end of the period axis. Industry values here have disclosure 3.

**`meta.provenance_boundaries.projections.from` string always present**

First quarter of the range, lower case, e.g. `2004q1`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.projections.to` string always present**

Last quarter of the range, lower case, e.g. `2025q4`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.geography` object**

Data routes only. Absent on taxonomy responses.

**`meta.geography.requested` integer always present**

/series and /lq: the number of distinct areas the request resolved to before entitlement (a `state:XX`, `msa:` or `cluster:` token counts its counties; a plain state code counts 1; no `areas` counts the nation as 1). /map: the counties of the `areas` restriction, or without one the counties with a row for this code and period (catch-alls never counted). (at least 0)

**`meta.geography.returned` integer always 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.excluded` integer always present**

Requested counties the organization's entitlement removed (outside the active county set, or every county while the subscription is past due). On /map a requested-versus-returned gap beyond `excluded` means counties with no data for the code and period, not an exclusion. (at least 0)

**`meta.geography.region` DataRegion**

Present only on a request whose areas were summed into one series (a lone `cluster:<id>` or `msa:<code>`, or `sum=areas` / `combine=sum`): what the summed area is made of. The CSV preamble prints it as `# region:` lines and the workbook's Source sheet as `Region` rows.

**`meta.geography.region.label` string always present**

The summed series' `area` label: the cluster's name or the MSA's title when one was asked for alone, otherwise `custom region`.

**`meta.geography.region.areas` array of DataRegionArea always present**

Each area as requested, in request order.

**`meta.geography.region.areas[].token` string always present**

The token exactly as requested: `22073`, `22`, `00`, `state:22`, `msa:35380`, `cluster:<id>`.

**`meta.geography.region.areas[].kind` "nation" | "state" | "county" | "stateAll" | "msa" | "cluster" always present**

What the token is: the nation (`00`), a state's own row (`22`), a county, `stateAll` (a `state:XX` token: every county of the state, its catch-all included), an MSA, or one of the organization's area clusters.

**`meta.geography.region.areas[].code` string always present**

The area's code: a FIPS code for a county or state (the 2-digit state code for `stateAll`), or the token itself, lower-cased, for `msa:<code>` and `cluster:<id>`.

**`meta.geography.region.areas[].name` string or null always 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[].resolved` array of string always present**

The area codes this token contributed to the sum after entitlement. A county the plan does not cover appears in no list; `geography.excluded` counts it.

**`meta.geography.region.resolved` array of string always present**

Every area code summed, deduplicated, in request order, after entitlement.

**`meta.pagination` DataPagination always present**

**`meta.pagination.cursor` string or null always present**

The `cursor` this page was requested with; null for the first page or an unpaged request.

**`meta.pagination.next_cursor` string or null always present**

Pass as `cursor` to get the next page. Null on the last page and on every unpaged response, so null always means there is nothing more.

**`meta.pagination.count` integer always present**

Rows in `data` on this response: series on /series and /lq, rows on /map, items on a taxonomy. (at least 0)

#### `304` redirect

Not modified: the `If-None-Match` tag still matches. No body.

**`ETag` header**

The current tag.

#### `400` error

Bad request. Codes: [`unknown_parameter`](/docs/api/errors#code-unknown-parameter) (a query parameter this route does not take; `parameters[]` names them), [`repeated_parameter`](/docs/api/errors#code-repeated-parameter) (a parameter given more than once; `parameter`), [`bad_request`](/docs/api/errors#code-bad-request) (more than 50 query parameters), [`bad_request`](/docs/api/errors#code-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).

As `application/problem+json`.

#### `401` error

Unauthenticated. Codes: [`unauthenticated`](/docs/api/errors#code-unauthenticated) (no credential, or an API key or session that is unknown or expired); [`key_revoked`](/docs/api/errors#code-key-revoked) (an API key that was revoked: `revoked_at`, and `reason` -- `holder`, `admin`, `member_removed`, `seat_removed`, `api_disabled`, `addon_ended`, [`account_closed`](/docs/api/errors#code-account-closed), [`org_closed`](/docs/api/errors#code-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`](/docs/api/errors#code-no-membership) (the account has no active organization membership), [`account_suspended`](/docs/api/errors#code-account-suspended) (O56: the caller's own account is suspended by DTG; `reason` where recorded), [`org_suspended`](/docs/api/errors#code-org-suspended) (the organization is suspended; `organization` {id, name} and, where recorded, `reason`), [`org_closed`](/docs/api/errors#code-org-closed) (the organization is closed; `organization`), [`wrong_instance`](/docs/api/errors#code-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).

As `application/problem+json`.

#### `404` error

Not found. Codes: [`not_found`](/docs/api/errors#code-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`](/docs/api/errors#code-vintage-superseded) (the pinned vintage was withdrawn and replaced by a correction; `superseded_by` names it; re-run against it), [`vintage_withdrawn`](/docs/api/errors#code-vintage-withdrawn) (the pinned vintage was withdrawn and no correction exists yet; `current` names the vintage to use).

As `application/problem+json`.

#### `414` error

URI too long. Codes: [`uri_too_long`](/docs/api/errors#code-uri-too-long) (the request URL is longer than the configured maximum, 8,192 bytes by default).

As `application/problem+json`.

#### `429` error

Rate limited, and only that: [`rate_limited`](/docs/api/errors#code-rate-limited) (over the 120 requests a minute per credential or 300 a minute per organization; the sandbox ceilings are lower). `Retry-After` and `X-RateLimit-*` say when to retry. This route is not admission-gated or metered.

As `application/problem+json`.

**`Retry-After` header**

Seconds to wait before retrying.

**`X-RateLimit-Limit` header**

The per-minute request ceiling that applies.

**`X-RateLimit-Remaining` header**

Requests left in the current window.

**`X-RateLimit-Reset` header**

Seconds until the window refills: on every answer, the time until the bucket is full again; on a refusal, until a request is allowed (as `Retry-After`).

#### `500` error

Server error. Codes: [`internal_error`](/docs/api/errors#code-internal-error) (for example a vintage file the server cannot read).

As `application/problem+json`.

Problem codes: [`unknown_parameter`](/docs/api/errors#code-unknown-parameter) (400), [`repeated_parameter`](/docs/api/errors#code-repeated-parameter) (400), [`bad_request`](/docs/api/errors#code-bad-request) (400), [`unauthenticated`](/docs/api/errors#code-unauthenticated) (401), [`key_revoked`](/docs/api/errors#code-key-revoked) (401), [`account_closed`](/docs/api/errors#code-account-closed) (401), [`org_closed`](/docs/api/errors#code-org-closed) (401), [`no_membership`](/docs/api/errors#code-no-membership) (403), [`account_suspended`](/docs/api/errors#code-account-suspended) (403), [`org_suspended`](/docs/api/errors#code-org-suspended) (403), [`org_closed`](/docs/api/errors#code-org-closed) (403), [`wrong_instance`](/docs/api/errors#code-wrong-instance) (403), [`not_found`](/docs/api/errors#code-not-found) (404), [`vintage_superseded`](/docs/api/errors#code-vintage-superseded) (404), [`vintage_withdrawn`](/docs/api/errors#code-vintage-withdrawn) (404), [`uri_too_long`](/docs/api/errors#code-uri-too-long) (414), [`rate_limited`](/docs/api/errors#code-rate-limited) (429), [`internal_error`](/docs/api/errors#code-internal-error) (500). Every problem is a [problem document](/docs/api/errors).

## GET `/v2/taxonomies/areas/levels`

**The area hierarchy's level names**

Credentials: API key or App session. Operation id `dataListAreaLevels`.

The names behind `level` on area items and the `fips` hierarchy (0 Nation, 1 State, 2 County), from the vintage's level lookup. Never paged. Any authenticated caller: an API key (live or sandbox) or a session, any role. Taxonomies are free: not behind the entitlement gate, not admission-gated, not metered, and not limited by plan (a non-current `vintage=` needs no prior-vintage entitlement here; a withdrawn or superseded one is still refused). A sandbox key reads the sandbox vintage's taxonomy, which is real apart from its five invented counties and MSAs. Caching: with an explicit `vintage=` the answer can never change and is `Cache-Control: public, max-age=31536000, immutable`; without it, `no-cache` with the same strong `ETag`, so revalidating with `If-None-Match` answers an empty 304 until a release moves the current vintage. Problems are never cached.

### Parameters

**`vintage` query · 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+)$)

**`X-DTG-Org` header · 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`](/docs/api/errors#code-not-found).

### Responses

#### `200` success

The levels, in level order.

As `application/json`.

**`X-RateLimit-Limit` header**

The per-minute request ceiling that applies.

**`X-RateLimit-Remaining` header**

Requests left in the current window.

**`X-RateLimit-Reset` header**

Seconds until the window refills: on every answer, the time until the bucket is full again; on a refusal, until a request is allowed (as `Retry-After`).

**`X-DTG-Sandbox` header**

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

**`ETag` header**

A strong tag over the body less `meta.generated_at`: the same for a pinned and an unpinned read of the same content and for every credential.

**`Cache-Control` header**

`public, max-age=31536000, immutable` when `vintage=` was given; `no-cache` otherwise.

**`data` array of DataLevelItem always present**

Every level, in level order.

**`data[].level` integer always present**

The level number used by `level` on taxonomy items and `naics_level` / `soc_level` on the data routes. (at least 0)

**`data[].name` string always present**

The level's name, e.g. `Sector` (NAICS 3), `Detailed occupation` (SOC 4), `County` (areas 2).

**`meta` DataMeta always present**

The `meta` of every v2 envelope (spec section 3). It is written after `data` on the wire. Clients must tolerate unknown fields.

**`meta.vintage` string always present**

The vintage the answer was read from: the internal build id (`20260913`; a sandbox key's are `sandbox-2026.1`). Cite it and pass it back as `vintage=` to reproduce the answer exactly: a released vintage never changes. Its public name, such as `DTG 2026.3`, is the `label` on `GET /v2/vintages`.

**`meta.sandbox` true**

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_at` string (date-time) always present**

When this response was produced, UTC, to the second (`2026-09-27T18:42:15Z`).

**`meta.granularity` "annual" | "quarterly"**

Data routes only: the period granularity of the points (on /map, `quarterly` when `qtr` was given). Absent on taxonomy responses.

**`meta.provenance_boundaries` DataProvenanceBoundaries always present**

Where the vintage's published record ends and estimation begins, recorded per vintage at build time and returned on every envelope. Compare a point's period against these ranges to know what kind of value it is. For OCCUPATION data this is the only provenance there is: occupation values carry no per-value label, so a period inside `actuals` is published-basis, inside `ces` carried forward, inside `projections` projected. Industry values also carry their own `disclosure` code, which is authoritative for the value. The ranges move with each release; read them, never hard-code them.

**`meta.provenance_boundaries.actuals` object always 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.from` string always present**

First quarter of the range, lower case, e.g. `2004q1`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.actuals.to` string always present**

Last quarter of the range, lower case, e.g. `2025q4`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.ces` object always 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.from` string always present**

First quarter of the range, lower case, e.g. `2004q1`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.ces.to` string always present**

Last quarter of the range, lower case, e.g. `2025q4`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.projections` object always present**

Projected quarters, out to the end of the period axis. Industry values here have disclosure 3.

**`meta.provenance_boundaries.projections.from` string always present**

First quarter of the range, lower case, e.g. `2004q1`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.projections.to` string always present**

Last quarter of the range, lower case, e.g. `2025q4`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.geography` object**

Data routes only. Absent on taxonomy responses.

**`meta.geography.requested` integer always present**

/series and /lq: the number of distinct areas the request resolved to before entitlement (a `state:XX`, `msa:` or `cluster:` token counts its counties; a plain state code counts 1; no `areas` counts the nation as 1). /map: the counties of the `areas` restriction, or without one the counties with a row for this code and period (catch-alls never counted). (at least 0)

**`meta.geography.returned` integer always 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.excluded` integer always present**

Requested counties the organization's entitlement removed (outside the active county set, or every county while the subscription is past due). On /map a requested-versus-returned gap beyond `excluded` means counties with no data for the code and period, not an exclusion. (at least 0)

**`meta.geography.region` DataRegion**

Present only on a request whose areas were summed into one series (a lone `cluster:<id>` or `msa:<code>`, or `sum=areas` / `combine=sum`): what the summed area is made of. The CSV preamble prints it as `# region:` lines and the workbook's Source sheet as `Region` rows.

**`meta.geography.region.label` string always present**

The summed series' `area` label: the cluster's name or the MSA's title when one was asked for alone, otherwise `custom region`.

**`meta.geography.region.areas` array of DataRegionArea always present**

Each area as requested, in request order.

**`meta.geography.region.areas[].token` string always present**

The token exactly as requested: `22073`, `22`, `00`, `state:22`, `msa:35380`, `cluster:<id>`.

**`meta.geography.region.areas[].kind` "nation" | "state" | "county" | "stateAll" | "msa" | "cluster" always present**

What the token is: the nation (`00`), a state's own row (`22`), a county, `stateAll` (a `state:XX` token: every county of the state, its catch-all included), an MSA, or one of the organization's area clusters.

**`meta.geography.region.areas[].code` string always present**

The area's code: a FIPS code for a county or state (the 2-digit state code for `stateAll`), or the token itself, lower-cased, for `msa:<code>` and `cluster:<id>`.

**`meta.geography.region.areas[].name` string or null always 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[].resolved` array of string always present**

The area codes this token contributed to the sum after entitlement. A county the plan does not cover appears in no list; `geography.excluded` counts it.

**`meta.geography.region.resolved` array of string always present**

Every area code summed, deduplicated, in request order, after entitlement.

**`meta.pagination` DataPagination always present**

**`meta.pagination.cursor` string or null always present**

The `cursor` this page was requested with; null for the first page or an unpaged request.

**`meta.pagination.next_cursor` string or null always present**

Pass as `cursor` to get the next page. Null on the last page and on every unpaged response, so null always means there is nothing more.

**`meta.pagination.count` integer always present**

Rows in `data` on this response: series on /series and /lq, rows on /map, items on a taxonomy. (at least 0)

#### `304` redirect

Not modified: the `If-None-Match` tag still matches. No body.

**`ETag` header**

The current tag.

#### `400` error

Bad request. Codes: [`unknown_parameter`](/docs/api/errors#code-unknown-parameter) (a query parameter this route does not take; `parameters[]` names them), [`repeated_parameter`](/docs/api/errors#code-repeated-parameter) (a parameter given more than once; `parameter`), [`bad_request`](/docs/api/errors#code-bad-request) (more than 50 query parameters), [`bad_request`](/docs/api/errors#code-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).

As `application/problem+json`.

#### `401` error

Unauthenticated. Codes: [`unauthenticated`](/docs/api/errors#code-unauthenticated) (no credential, or an API key or session that is unknown or expired); [`key_revoked`](/docs/api/errors#code-key-revoked) (an API key that was revoked: `revoked_at`, and `reason` -- `holder`, `admin`, `member_removed`, `seat_removed`, `api_disabled`, `addon_ended`, [`account_closed`](/docs/api/errors#code-account-closed), [`org_closed`](/docs/api/errors#code-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`](/docs/api/errors#code-no-membership) (the account has no active organization membership), [`account_suspended`](/docs/api/errors#code-account-suspended) (O56: the caller's own account is suspended by DTG; `reason` where recorded), [`org_suspended`](/docs/api/errors#code-org-suspended) (the organization is suspended; `organization` {id, name} and, where recorded, `reason`), [`org_closed`](/docs/api/errors#code-org-closed) (the organization is closed; `organization`), [`wrong_instance`](/docs/api/errors#code-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).

As `application/problem+json`.

#### `404` error

Not found. Codes: [`not_found`](/docs/api/errors#code-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`](/docs/api/errors#code-vintage-superseded) (the pinned vintage was withdrawn and replaced by a correction; `superseded_by` names it; re-run against it), [`vintage_withdrawn`](/docs/api/errors#code-vintage-withdrawn) (the pinned vintage was withdrawn and no correction exists yet; `current` names the vintage to use).

As `application/problem+json`.

#### `414` error

URI too long. Codes: [`uri_too_long`](/docs/api/errors#code-uri-too-long) (the request URL is longer than the configured maximum, 8,192 bytes by default).

As `application/problem+json`.

#### `429` error

Rate limited, and only that: [`rate_limited`](/docs/api/errors#code-rate-limited) (over the 120 requests a minute per credential or 300 a minute per organization; the sandbox ceilings are lower). `Retry-After` and `X-RateLimit-*` say when to retry. This route is not admission-gated or metered.

As `application/problem+json`.

**`Retry-After` header**

Seconds to wait before retrying.

**`X-RateLimit-Limit` header**

The per-minute request ceiling that applies.

**`X-RateLimit-Remaining` header**

Requests left in the current window.

**`X-RateLimit-Reset` header**

Seconds until the window refills: on every answer, the time until the bucket is full again; on a refusal, until a request is allowed (as `Retry-After`).

#### `500` error

Server error. Codes: [`internal_error`](/docs/api/errors#code-internal-error) (for example a vintage file the server cannot read).

As `application/problem+json`.

Problem codes: [`unknown_parameter`](/docs/api/errors#code-unknown-parameter) (400), [`repeated_parameter`](/docs/api/errors#code-repeated-parameter) (400), [`bad_request`](/docs/api/errors#code-bad-request) (400), [`unauthenticated`](/docs/api/errors#code-unauthenticated) (401), [`key_revoked`](/docs/api/errors#code-key-revoked) (401), [`account_closed`](/docs/api/errors#code-account-closed) (401), [`org_closed`](/docs/api/errors#code-org-closed) (401), [`no_membership`](/docs/api/errors#code-no-membership) (403), [`account_suspended`](/docs/api/errors#code-account-suspended) (403), [`org_suspended`](/docs/api/errors#code-org-suspended) (403), [`org_closed`](/docs/api/errors#code-org-closed) (403), [`wrong_instance`](/docs/api/errors#code-wrong-instance) (403), [`not_found`](/docs/api/errors#code-not-found) (404), [`vintage_superseded`](/docs/api/errors#code-vintage-superseded) (404), [`vintage_withdrawn`](/docs/api/errors#code-vintage-withdrawn) (404), [`uri_too_long`](/docs/api/errors#code-uri-too-long) (414), [`rate_limited`](/docs/api/errors#code-rate-limited) (429), [`internal_error`](/docs/api/errors#code-internal-error) (500). Every problem is a [problem document](/docs/api/errors).

## GET `/v2/taxonomies/naics`

**NAICS industry codes**

Credentials: API key or App session. Operation id `dataListNaics`.

The vintage's NAICS platform codes in nested order (a parent before its children), filtered and paged. Filters combine (AND). Any authenticated caller: an API key (live or sandbox) or a session, any role. Taxonomies are free: not behind the entitlement gate, not admission-gated, not metered, and not limited by plan (a non-current `vintage=` needs no prior-vintage entitlement here; a withdrawn or superseded one is still refused). A sandbox key reads the sandbox vintage's taxonomy, which is real apart from its five invented counties and MSAs. Caching: with an explicit `vintage=` the answer can never change and is `Cache-Control: public, max-age=31536000, immutable`; without it, `no-cache` with the same strong `ETag`, so revalidating with `If-None-Match` answers an empty 304 until a release moves the current vintage. Problems are never cached.

### Parameters

**`level` query · integer**

Only codes at this hierarchy level (0 to 7; see `/v2/taxonomies/naics/levels`). (0 to 7)

**`parent` query · string**

Only the direct children of this NAICS platform code (digits only, e.g. `31`). (pattern ^\\d+$)

**`q` query · string**

Search: codes whose title contains this text (case-insensitive) or whose code starts with it.

**`leaf` query · "true" | "false"**

`true`: only codes that are the parent of no code (the lowest set); `false`: only parents.

**`cursor` query · string**

Opaque cursor from `meta.pagination.next_cursor` of the previous page.

**`limit` query · integer**

Items per page, 1 to 5,000 (default 1,000). (default 1000; 1 to 5000)

**`vintage` query · 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+)$)

**`X-DTG-Org` header · 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`](/docs/api/errors#code-not-found).

### Responses

#### `200` success

A page of NAICS codes.

As `application/json`.

**`X-RateLimit-Limit` header**

The per-minute request ceiling that applies.

**`X-RateLimit-Remaining` header**

Requests left in the current window.

**`X-RateLimit-Reset` header**

Seconds until the window refills: on every answer, the time until the bucket is full again; on a refusal, until a request is allowed (as `Retry-After`).

**`X-DTG-Sandbox` header**

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

**`ETag` header**

A strong tag over the body less `meta.generated_at`: the same for a pinned and an unpinned read of the same content and for every credential.

**`Cache-Control` header**

`public, max-age=31536000, immutable` when `vintage=` was given; `no-cache` otherwise.

**`data` array of DataTaxonomyItem always present**

The codes on this page, in nested order.

**`data[].code` string always present**

The code to use in requests. NAICS: the platform code (`10` is the total, all industries; `101`/`102` the domains; `1011`..`1029` the super-sectors; then the standard 2- to 6-digit codes, with each range sector stored under its first code, `31` for 31-33). SOC: `11-1011` form; `00-0000` is the total.

**`data[].display_code` string**

NAICS only: the code as BLS prints it, `31-33`, `44-45` or `48-49` for the range sectors, otherwise equal to `code`. For display; `code` stays the identity.

**`data[].title` string or null always present**

The code's title in this vintage.

**`data[].level` integer always present**

The position in the BLS hierarchy, named by `GET /v2/taxonomies/{naics|soc}/levels` (not a digit count). NAICS: 0 Total, 1 Domain, 2 Super-sector, 3 Sector (2-digit), 4 to 7 the 3- to 6-digit codes. SOC: 0 Total, 1 Major group, 2 Minor group, 3 Broad occupation, 4 Detailed occupation. (at least 0)

**`data[].parent` string or null always present**

The parent code; null at the root (the total).

**`data[].nested_order` integer always present**

The code's position in the depth-first order of the hierarchy; series answers sort codes by it.

**`data[].is_leaf` boolean always present**

True when the code is the parent of no code (the lowest set: NAICS 6-digit codes; SOC detailed occupations and the broad occupations with no children). `naics_level=leaf` / `soc_level=leaf` on the data routes selects exactly these.

**`meta` DataMeta always present**

The `meta` of every v2 envelope (spec section 3). It is written after `data` on the wire. Clients must tolerate unknown fields.

**`meta.vintage` string always present**

The vintage the answer was read from: the internal build id (`20260913`; a sandbox key's are `sandbox-2026.1`). Cite it and pass it back as `vintage=` to reproduce the answer exactly: a released vintage never changes. Its public name, such as `DTG 2026.3`, is the `label` on `GET /v2/vintages`.

**`meta.sandbox` true**

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_at` string (date-time) always present**

When this response was produced, UTC, to the second (`2026-09-27T18:42:15Z`).

**`meta.granularity` "annual" | "quarterly"**

Data routes only: the period granularity of the points (on /map, `quarterly` when `qtr` was given). Absent on taxonomy responses.

**`meta.provenance_boundaries` DataProvenanceBoundaries always present**

Where the vintage's published record ends and estimation begins, recorded per vintage at build time and returned on every envelope. Compare a point's period against these ranges to know what kind of value it is. For OCCUPATION data this is the only provenance there is: occupation values carry no per-value label, so a period inside `actuals` is published-basis, inside `ces` carried forward, inside `projections` projected. Industry values also carry their own `disclosure` code, which is authoritative for the value. The ranges move with each release; read them, never hard-code them.

**`meta.provenance_boundaries.actuals` object always 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.from` string always present**

First quarter of the range, lower case, e.g. `2004q1`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.actuals.to` string always present**

Last quarter of the range, lower case, e.g. `2025q4`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.ces` object always 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.from` string always present**

First quarter of the range, lower case, e.g. `2004q1`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.ces.to` string always present**

Last quarter of the range, lower case, e.g. `2025q4`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.projections` object always present**

Projected quarters, out to the end of the period axis. Industry values here have disclosure 3.

**`meta.provenance_boundaries.projections.from` string always present**

First quarter of the range, lower case, e.g. `2004q1`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.projections.to` string always present**

Last quarter of the range, lower case, e.g. `2025q4`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.geography` object**

Data routes only. Absent on taxonomy responses.

**`meta.geography.requested` integer always present**

/series and /lq: the number of distinct areas the request resolved to before entitlement (a `state:XX`, `msa:` or `cluster:` token counts its counties; a plain state code counts 1; no `areas` counts the nation as 1). /map: the counties of the `areas` restriction, or without one the counties with a row for this code and period (catch-alls never counted). (at least 0)

**`meta.geography.returned` integer always 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.excluded` integer always present**

Requested counties the organization's entitlement removed (outside the active county set, or every county while the subscription is past due). On /map a requested-versus-returned gap beyond `excluded` means counties with no data for the code and period, not an exclusion. (at least 0)

**`meta.geography.region` DataRegion**

Present only on a request whose areas were summed into one series (a lone `cluster:<id>` or `msa:<code>`, or `sum=areas` / `combine=sum`): what the summed area is made of. The CSV preamble prints it as `# region:` lines and the workbook's Source sheet as `Region` rows.

**`meta.geography.region.label` string always present**

The summed series' `area` label: the cluster's name or the MSA's title when one was asked for alone, otherwise `custom region`.

**`meta.geography.region.areas` array of DataRegionArea always present**

Each area as requested, in request order.

**`meta.geography.region.areas[].token` string always present**

The token exactly as requested: `22073`, `22`, `00`, `state:22`, `msa:35380`, `cluster:<id>`.

**`meta.geography.region.areas[].kind` "nation" | "state" | "county" | "stateAll" | "msa" | "cluster" always present**

What the token is: the nation (`00`), a state's own row (`22`), a county, `stateAll` (a `state:XX` token: every county of the state, its catch-all included), an MSA, or one of the organization's area clusters.

**`meta.geography.region.areas[].code` string always present**

The area's code: a FIPS code for a county or state (the 2-digit state code for `stateAll`), or the token itself, lower-cased, for `msa:<code>` and `cluster:<id>`.

**`meta.geography.region.areas[].name` string or null always 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[].resolved` array of string always present**

The area codes this token contributed to the sum after entitlement. A county the plan does not cover appears in no list; `geography.excluded` counts it.

**`meta.geography.region.resolved` array of string always present**

Every area code summed, deduplicated, in request order, after entitlement.

**`meta.pagination` DataPagination always present**

**`meta.pagination.cursor` string or null always present**

The `cursor` this page was requested with; null for the first page or an unpaged request.

**`meta.pagination.next_cursor` string or null always present**

Pass as `cursor` to get the next page. Null on the last page and on every unpaged response, so null always means there is nothing more.

**`meta.pagination.count` integer always present**

Rows in `data` on this response: series on /series and /lq, rows on /map, items on a taxonomy. (at least 0)

#### `304` redirect

Not modified: the `If-None-Match` tag still matches. No body.

**`ETag` header**

The current tag.

#### `400` error

Bad request. Codes: [`unknown_parameter`](/docs/api/errors#code-unknown-parameter) (a query parameter this route does not take; `parameters[]` names them), [`repeated_parameter`](/docs/api/errors#code-repeated-parameter) (a parameter given more than once; `parameter`), [`bad_request`](/docs/api/errors#code-bad-request) (more than 50 query parameters), [`bad_request`](/docs/api/errors#code-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`](/docs/api/errors#code-bad-request) for a `level` that is not an integer from 0 to 7, a `leaf` other than true or false, or a non-numeric `parent`, [`bad_request`](/docs/api/errors#code-bad-request) for a bad `cursor` or a `limit` outside 1 to 5,000.

As `application/problem+json`.

#### `401` error

Unauthenticated. Codes: [`unauthenticated`](/docs/api/errors#code-unauthenticated) (no credential, or an API key or session that is unknown or expired); [`key_revoked`](/docs/api/errors#code-key-revoked) (an API key that was revoked: `revoked_at`, and `reason` -- `holder`, `admin`, `member_removed`, `seat_removed`, `api_disabled`, `addon_ended`, [`account_closed`](/docs/api/errors#code-account-closed), [`org_closed`](/docs/api/errors#code-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`](/docs/api/errors#code-no-membership) (the account has no active organization membership), [`account_suspended`](/docs/api/errors#code-account-suspended) (O56: the caller's own account is suspended by DTG; `reason` where recorded), [`org_suspended`](/docs/api/errors#code-org-suspended) (the organization is suspended; `organization` {id, name} and, where recorded, `reason`), [`org_closed`](/docs/api/errors#code-org-closed) (the organization is closed; `organization`), [`wrong_instance`](/docs/api/errors#code-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).

As `application/problem+json`.

#### `404` error

Not found. Codes: [`not_found`](/docs/api/errors#code-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`](/docs/api/errors#code-vintage-superseded) (the pinned vintage was withdrawn and replaced by a correction; `superseded_by` names it; re-run against it), [`vintage_withdrawn`](/docs/api/errors#code-vintage-withdrawn) (the pinned vintage was withdrawn and no correction exists yet; `current` names the vintage to use).

As `application/problem+json`.

#### `414` error

URI too long. Codes: [`uri_too_long`](/docs/api/errors#code-uri-too-long) (the request URL is longer than the configured maximum, 8,192 bytes by default).

As `application/problem+json`.

#### `429` error

Rate limited, and only that: [`rate_limited`](/docs/api/errors#code-rate-limited) (over the 120 requests a minute per credential or 300 a minute per organization; the sandbox ceilings are lower). `Retry-After` and `X-RateLimit-*` say when to retry. This route is not admission-gated or metered.

As `application/problem+json`.

**`Retry-After` header**

Seconds to wait before retrying.

**`X-RateLimit-Limit` header**

The per-minute request ceiling that applies.

**`X-RateLimit-Remaining` header**

Requests left in the current window.

**`X-RateLimit-Reset` header**

Seconds until the window refills: on every answer, the time until the bucket is full again; on a refusal, until a request is allowed (as `Retry-After`).

#### `500` error

Server error. Codes: [`internal_error`](/docs/api/errors#code-internal-error) (for example a vintage file the server cannot read).

As `application/problem+json`.

Problem codes: [`unknown_parameter`](/docs/api/errors#code-unknown-parameter) (400), [`repeated_parameter`](/docs/api/errors#code-repeated-parameter) (400), [`bad_request`](/docs/api/errors#code-bad-request) (400), [`unauthenticated`](/docs/api/errors#code-unauthenticated) (401), [`key_revoked`](/docs/api/errors#code-key-revoked) (401), [`account_closed`](/docs/api/errors#code-account-closed) (401), [`org_closed`](/docs/api/errors#code-org-closed) (401), [`no_membership`](/docs/api/errors#code-no-membership) (403), [`account_suspended`](/docs/api/errors#code-account-suspended) (403), [`org_suspended`](/docs/api/errors#code-org-suspended) (403), [`org_closed`](/docs/api/errors#code-org-closed) (403), [`wrong_instance`](/docs/api/errors#code-wrong-instance) (403), [`not_found`](/docs/api/errors#code-not-found) (404), [`vintage_superseded`](/docs/api/errors#code-vintage-superseded) (404), [`vintage_withdrawn`](/docs/api/errors#code-vintage-withdrawn) (404), [`uri_too_long`](/docs/api/errors#code-uri-too-long) (414), [`rate_limited`](/docs/api/errors#code-rate-limited) (429), [`internal_error`](/docs/api/errors#code-internal-error) (500). Every problem is a [problem document](/docs/api/errors).

## GET `/v2/taxonomies/soc`

**SOC occupation codes**

Credentials: API key or App session. Operation id `dataListSoc`.

The vintage's SOC codes in nested order, filtered and paged. Filters combine (AND). Items carry no `display_code`. Any authenticated caller: an API key (live or sandbox) or a session, any role. Taxonomies are free: not behind the entitlement gate, not admission-gated, not metered, and not limited by plan (a non-current `vintage=` needs no prior-vintage entitlement here; a withdrawn or superseded one is still refused). A sandbox key reads the sandbox vintage's taxonomy, which is real apart from its five invented counties and MSAs. Caching: with an explicit `vintage=` the answer can never change and is `Cache-Control: public, max-age=31536000, immutable`; without it, `no-cache` with the same strong `ETag`, so revalidating with `If-None-Match` answers an empty 304 until a release moves the current vintage. Problems are never cached.

### Parameters

**`level` query · integer**

Only codes at this hierarchy level (0 to 4; see `/v2/taxonomies/soc/levels`). (0 to 4)

**`parent` query · string**

Only the direct children of this SOC code (e.g. `11-0000`).

**`q` query · string**

Search: codes whose title contains this text (case-insensitive) or whose code starts with it.

**`leaf` query · "true" | "false"**

`true`: only codes that are the parent of no code (the lowest set); `false`: only parents.

**`cursor` query · string**

Opaque cursor from `meta.pagination.next_cursor` of the previous page.

**`limit` query · integer**

Items per page, 1 to 5,000 (default 1,000). (default 1000; 1 to 5000)

**`vintage` query · 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+)$)

**`X-DTG-Org` header · 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`](/docs/api/errors#code-not-found).

### Responses

#### `200` success

A page of SOC codes.

As `application/json`.

**`X-RateLimit-Limit` header**

The per-minute request ceiling that applies.

**`X-RateLimit-Remaining` header**

Requests left in the current window.

**`X-RateLimit-Reset` header**

Seconds until the window refills: on every answer, the time until the bucket is full again; on a refusal, until a request is allowed (as `Retry-After`).

**`X-DTG-Sandbox` header**

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

**`ETag` header**

A strong tag over the body less `meta.generated_at`: the same for a pinned and an unpinned read of the same content and for every credential.

**`Cache-Control` header**

`public, max-age=31536000, immutable` when `vintage=` was given; `no-cache` otherwise.

**`data` array of DataTaxonomyItem always present**

The codes on this page, in nested order.

**`data[].code` string always present**

The code to use in requests. NAICS: the platform code (`10` is the total, all industries; `101`/`102` the domains; `1011`..`1029` the super-sectors; then the standard 2- to 6-digit codes, with each range sector stored under its first code, `31` for 31-33). SOC: `11-1011` form; `00-0000` is the total.

**`data[].display_code` string**

NAICS only: the code as BLS prints it, `31-33`, `44-45` or `48-49` for the range sectors, otherwise equal to `code`. For display; `code` stays the identity.

**`data[].title` string or null always present**

The code's title in this vintage.

**`data[].level` integer always present**

The position in the BLS hierarchy, named by `GET /v2/taxonomies/{naics|soc}/levels` (not a digit count). NAICS: 0 Total, 1 Domain, 2 Super-sector, 3 Sector (2-digit), 4 to 7 the 3- to 6-digit codes. SOC: 0 Total, 1 Major group, 2 Minor group, 3 Broad occupation, 4 Detailed occupation. (at least 0)

**`data[].parent` string or null always present**

The parent code; null at the root (the total).

**`data[].nested_order` integer always present**

The code's position in the depth-first order of the hierarchy; series answers sort codes by it.

**`data[].is_leaf` boolean always present**

True when the code is the parent of no code (the lowest set: NAICS 6-digit codes; SOC detailed occupations and the broad occupations with no children). `naics_level=leaf` / `soc_level=leaf` on the data routes selects exactly these.

**`meta` DataMeta always present**

The `meta` of every v2 envelope (spec section 3). It is written after `data` on the wire. Clients must tolerate unknown fields.

**`meta.vintage` string always present**

The vintage the answer was read from: the internal build id (`20260913`; a sandbox key's are `sandbox-2026.1`). Cite it and pass it back as `vintage=` to reproduce the answer exactly: a released vintage never changes. Its public name, such as `DTG 2026.3`, is the `label` on `GET /v2/vintages`.

**`meta.sandbox` true**

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_at` string (date-time) always present**

When this response was produced, UTC, to the second (`2026-09-27T18:42:15Z`).

**`meta.granularity` "annual" | "quarterly"**

Data routes only: the period granularity of the points (on /map, `quarterly` when `qtr` was given). Absent on taxonomy responses.

**`meta.provenance_boundaries` DataProvenanceBoundaries always present**

Where the vintage's published record ends and estimation begins, recorded per vintage at build time and returned on every envelope. Compare a point's period against these ranges to know what kind of value it is. For OCCUPATION data this is the only provenance there is: occupation values carry no per-value label, so a period inside `actuals` is published-basis, inside `ces` carried forward, inside `projections` projected. Industry values also carry their own `disclosure` code, which is authoritative for the value. The ranges move with each release; read them, never hard-code them.

**`meta.provenance_boundaries.actuals` object always 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.from` string always present**

First quarter of the range, lower case, e.g. `2004q1`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.actuals.to` string always present**

Last quarter of the range, lower case, e.g. `2025q4`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.ces` object always 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.from` string always present**

First quarter of the range, lower case, e.g. `2004q1`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.ces.to` string always present**

Last quarter of the range, lower case, e.g. `2025q4`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.projections` object always present**

Projected quarters, out to the end of the period axis. Industry values here have disclosure 3.

**`meta.provenance_boundaries.projections.from` string always present**

First quarter of the range, lower case, e.g. `2004q1`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.projections.to` string always present**

Last quarter of the range, lower case, e.g. `2025q4`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.geography` object**

Data routes only. Absent on taxonomy responses.

**`meta.geography.requested` integer always present**

/series and /lq: the number of distinct areas the request resolved to before entitlement (a `state:XX`, `msa:` or `cluster:` token counts its counties; a plain state code counts 1; no `areas` counts the nation as 1). /map: the counties of the `areas` restriction, or without one the counties with a row for this code and period (catch-alls never counted). (at least 0)

**`meta.geography.returned` integer always 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.excluded` integer always present**

Requested counties the organization's entitlement removed (outside the active county set, or every county while the subscription is past due). On /map a requested-versus-returned gap beyond `excluded` means counties with no data for the code and period, not an exclusion. (at least 0)

**`meta.geography.region` DataRegion**

Present only on a request whose areas were summed into one series (a lone `cluster:<id>` or `msa:<code>`, or `sum=areas` / `combine=sum`): what the summed area is made of. The CSV preamble prints it as `# region:` lines and the workbook's Source sheet as `Region` rows.

**`meta.geography.region.label` string always present**

The summed series' `area` label: the cluster's name or the MSA's title when one was asked for alone, otherwise `custom region`.

**`meta.geography.region.areas` array of DataRegionArea always present**

Each area as requested, in request order.

**`meta.geography.region.areas[].token` string always present**

The token exactly as requested: `22073`, `22`, `00`, `state:22`, `msa:35380`, `cluster:<id>`.

**`meta.geography.region.areas[].kind` "nation" | "state" | "county" | "stateAll" | "msa" | "cluster" always present**

What the token is: the nation (`00`), a state's own row (`22`), a county, `stateAll` (a `state:XX` token: every county of the state, its catch-all included), an MSA, or one of the organization's area clusters.

**`meta.geography.region.areas[].code` string always present**

The area's code: a FIPS code for a county or state (the 2-digit state code for `stateAll`), or the token itself, lower-cased, for `msa:<code>` and `cluster:<id>`.

**`meta.geography.region.areas[].name` string or null always 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[].resolved` array of string always present**

The area codes this token contributed to the sum after entitlement. A county the plan does not cover appears in no list; `geography.excluded` counts it.

**`meta.geography.region.resolved` array of string always present**

Every area code summed, deduplicated, in request order, after entitlement.

**`meta.pagination` DataPagination always present**

**`meta.pagination.cursor` string or null always present**

The `cursor` this page was requested with; null for the first page or an unpaged request.

**`meta.pagination.next_cursor` string or null always present**

Pass as `cursor` to get the next page. Null on the last page and on every unpaged response, so null always means there is nothing more.

**`meta.pagination.count` integer always present**

Rows in `data` on this response: series on /series and /lq, rows on /map, items on a taxonomy. (at least 0)

#### `304` redirect

Not modified: the `If-None-Match` tag still matches. No body.

**`ETag` header**

The current tag.

#### `400` error

Bad request. Codes: [`unknown_parameter`](/docs/api/errors#code-unknown-parameter) (a query parameter this route does not take; `parameters[]` names them), [`repeated_parameter`](/docs/api/errors#code-repeated-parameter) (a parameter given more than once; `parameter`), [`bad_request`](/docs/api/errors#code-bad-request) (more than 50 query parameters), [`bad_request`](/docs/api/errors#code-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`](/docs/api/errors#code-bad-request) for a `level` that is not an integer from 0 to 4 or a `leaf` other than true or false, [`bad_request`](/docs/api/errors#code-bad-request) for a bad `cursor` or a `limit` outside 1 to 5,000.

As `application/problem+json`.

#### `401` error

Unauthenticated. Codes: [`unauthenticated`](/docs/api/errors#code-unauthenticated) (no credential, or an API key or session that is unknown or expired); [`key_revoked`](/docs/api/errors#code-key-revoked) (an API key that was revoked: `revoked_at`, and `reason` -- `holder`, `admin`, `member_removed`, `seat_removed`, `api_disabled`, `addon_ended`, [`account_closed`](/docs/api/errors#code-account-closed), [`org_closed`](/docs/api/errors#code-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`](/docs/api/errors#code-no-membership) (the account has no active organization membership), [`account_suspended`](/docs/api/errors#code-account-suspended) (O56: the caller's own account is suspended by DTG; `reason` where recorded), [`org_suspended`](/docs/api/errors#code-org-suspended) (the organization is suspended; `organization` {id, name} and, where recorded, `reason`), [`org_closed`](/docs/api/errors#code-org-closed) (the organization is closed; `organization`), [`wrong_instance`](/docs/api/errors#code-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).

As `application/problem+json`.

#### `404` error

Not found. Codes: [`not_found`](/docs/api/errors#code-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`](/docs/api/errors#code-vintage-superseded) (the pinned vintage was withdrawn and replaced by a correction; `superseded_by` names it; re-run against it), [`vintage_withdrawn`](/docs/api/errors#code-vintage-withdrawn) (the pinned vintage was withdrawn and no correction exists yet; `current` names the vintage to use).

As `application/problem+json`.

#### `414` error

URI too long. Codes: [`uri_too_long`](/docs/api/errors#code-uri-too-long) (the request URL is longer than the configured maximum, 8,192 bytes by default).

As `application/problem+json`.

#### `429` error

Rate limited, and only that: [`rate_limited`](/docs/api/errors#code-rate-limited) (over the 120 requests a minute per credential or 300 a minute per organization; the sandbox ceilings are lower). `Retry-After` and `X-RateLimit-*` say when to retry. This route is not admission-gated or metered.

As `application/problem+json`.

**`Retry-After` header**

Seconds to wait before retrying.

**`X-RateLimit-Limit` header**

The per-minute request ceiling that applies.

**`X-RateLimit-Remaining` header**

Requests left in the current window.

**`X-RateLimit-Reset` header**

Seconds until the window refills: on every answer, the time until the bucket is full again; on a refusal, until a request is allowed (as `Retry-After`).

#### `500` error

Server error. Codes: [`internal_error`](/docs/api/errors#code-internal-error) (for example a vintage file the server cannot read).

As `application/problem+json`.

Problem codes: [`unknown_parameter`](/docs/api/errors#code-unknown-parameter) (400), [`repeated_parameter`](/docs/api/errors#code-repeated-parameter) (400), [`bad_request`](/docs/api/errors#code-bad-request) (400), [`unauthenticated`](/docs/api/errors#code-unauthenticated) (401), [`key_revoked`](/docs/api/errors#code-key-revoked) (401), [`account_closed`](/docs/api/errors#code-account-closed) (401), [`org_closed`](/docs/api/errors#code-org-closed) (401), [`no_membership`](/docs/api/errors#code-no-membership) (403), [`account_suspended`](/docs/api/errors#code-account-suspended) (403), [`org_suspended`](/docs/api/errors#code-org-suspended) (403), [`org_closed`](/docs/api/errors#code-org-closed) (403), [`wrong_instance`](/docs/api/errors#code-wrong-instance) (403), [`not_found`](/docs/api/errors#code-not-found) (404), [`vintage_superseded`](/docs/api/errors#code-vintage-superseded) (404), [`vintage_withdrawn`](/docs/api/errors#code-vintage-withdrawn) (404), [`uri_too_long`](/docs/api/errors#code-uri-too-long) (414), [`rate_limited`](/docs/api/errors#code-rate-limited) (429), [`internal_error`](/docs/api/errors#code-internal-error) (500). Every problem is a [problem document](/docs/api/errors).

## GET `/v2/taxonomies/areas`

**Geographic areas (nation, states, counties)**

Credentials: API key or App session. Operation id `dataListAreas`.

The vintage's areas in FIPS order, filtered and paged: the nation, the states and DC, and every county-level area including the 50 per-state catch-alls. Filters combine (AND). Areas have no nested order. The list does not depend on the plan: entitlement applies when data is requested. Any authenticated caller: an API key (live or sandbox) or a session, any role. Taxonomies are free: not behind the entitlement gate, not admission-gated, not metered, and not limited by plan (a non-current `vintage=` needs no prior-vintage entitlement here; a withdrawn or superseded one is still refused). A sandbox key reads the sandbox vintage's taxonomy, which is real apart from its five invented counties and MSAs. Caching: with an explicit `vintage=` the answer can never change and is `Cache-Control: public, max-age=31536000, immutable`; without it, `no-cache` with the same strong `ETag`, so revalidating with `If-None-Match` answers an empty 304 until a release moves the current vintage. Problems are never cached.

### Parameters

**`level` query · integer**

Only areas at this level: 0 nation, 1 state, 2 county. (0 to 2)

**`parent` query · string**

Only the areas directly inside this area code: `00` lists the states, `22` the counties of Louisiana. (pattern ^(\\d{2}|\\d{5})$)

**`q` query · string**

Search: areas whose name contains this text (case-insensitive) or whose source FIPS code starts with it.

**`cursor` query · string**

Opaque cursor from `meta.pagination.next_cursor` of the previous page.

**`limit` query · integer**

Items per page, 1 to 5,000 (default 1,000). (default 1000; 1 to 5000)

**`vintage` query · 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+)$)

**`X-DTG-Org` header · 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`](/docs/api/errors#code-not-found).

### Responses

#### `200` success

A page of areas.

As `application/json`.

**`X-RateLimit-Limit` header**

The per-minute request ceiling that applies.

**`X-RateLimit-Remaining` header**

Requests left in the current window.

**`X-RateLimit-Reset` header**

Seconds until the window refills: on every answer, the time until the bucket is full again; on a refusal, until a request is allowed (as `Retry-After`).

**`X-DTG-Sandbox` header**

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

**`ETag` header**

A strong tag over the body less `meta.generated_at`: the same for a pinned and an unpinned read of the same content and for every credential.

**`Cache-Control` header**

`public, max-age=31536000, immutable` when `vintage=` was given; `no-cache` otherwise.

**`data` array of DataAreaItem always present**

The areas on this page, in FIPS order.

**`data[].code` string always present**

The FIPS code, zero-padded: `00` nation, `22` state, `22073` county.

**`data[].title` string or null always present**

The area's name (`Ouachita Parish, Louisiana` style; `Undefined` for a catch-all).

**`data[].level` 0 | 1 | 2 always present**

0 nation, 1 state, 2 county (names on `GET /v2/taxonomies/areas/levels`).

**`data[].parent` string or null always present**

The containing area's code: `00` for a state, the state code for a county; null for the nation.

**`data[].type` string or null always present**

The source's area type label (for example `County`; catch-alls are typed `County` too, so do not use it to find them).

**`meta` DataMeta always present**

The `meta` of every v2 envelope (spec section 3). It is written after `data` on the wire. Clients must tolerate unknown fields.

**`meta.vintage` string always present**

The vintage the answer was read from: the internal build id (`20260913`; a sandbox key's are `sandbox-2026.1`). Cite it and pass it back as `vintage=` to reproduce the answer exactly: a released vintage never changes. Its public name, such as `DTG 2026.3`, is the `label` on `GET /v2/vintages`.

**`meta.sandbox` true**

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_at` string (date-time) always present**

When this response was produced, UTC, to the second (`2026-09-27T18:42:15Z`).

**`meta.granularity` "annual" | "quarterly"**

Data routes only: the period granularity of the points (on /map, `quarterly` when `qtr` was given). Absent on taxonomy responses.

**`meta.provenance_boundaries` DataProvenanceBoundaries always present**

Where the vintage's published record ends and estimation begins, recorded per vintage at build time and returned on every envelope. Compare a point's period against these ranges to know what kind of value it is. For OCCUPATION data this is the only provenance there is: occupation values carry no per-value label, so a period inside `actuals` is published-basis, inside `ces` carried forward, inside `projections` projected. Industry values also carry their own `disclosure` code, which is authoritative for the value. The ranges move with each release; read them, never hard-code them.

**`meta.provenance_boundaries.actuals` object always 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.from` string always present**

First quarter of the range, lower case, e.g. `2004q1`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.actuals.to` string always present**

Last quarter of the range, lower case, e.g. `2025q4`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.ces` object always 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.from` string always present**

First quarter of the range, lower case, e.g. `2004q1`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.ces.to` string always present**

Last quarter of the range, lower case, e.g. `2025q4`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.projections` object always present**

Projected quarters, out to the end of the period axis. Industry values here have disclosure 3.

**`meta.provenance_boundaries.projections.from` string always present**

First quarter of the range, lower case, e.g. `2004q1`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.projections.to` string always present**

Last quarter of the range, lower case, e.g. `2025q4`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.geography` object**

Data routes only. Absent on taxonomy responses.

**`meta.geography.requested` integer always present**

/series and /lq: the number of distinct areas the request resolved to before entitlement (a `state:XX`, `msa:` or `cluster:` token counts its counties; a plain state code counts 1; no `areas` counts the nation as 1). /map: the counties of the `areas` restriction, or without one the counties with a row for this code and period (catch-alls never counted). (at least 0)

**`meta.geography.returned` integer always 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.excluded` integer always present**

Requested counties the organization's entitlement removed (outside the active county set, or every county while the subscription is past due). On /map a requested-versus-returned gap beyond `excluded` means counties with no data for the code and period, not an exclusion. (at least 0)

**`meta.geography.region` DataRegion**

Present only on a request whose areas were summed into one series (a lone `cluster:<id>` or `msa:<code>`, or `sum=areas` / `combine=sum`): what the summed area is made of. The CSV preamble prints it as `# region:` lines and the workbook's Source sheet as `Region` rows.

**`meta.geography.region.label` string always present**

The summed series' `area` label: the cluster's name or the MSA's title when one was asked for alone, otherwise `custom region`.

**`meta.geography.region.areas` array of DataRegionArea always present**

Each area as requested, in request order.

**`meta.geography.region.areas[].token` string always present**

The token exactly as requested: `22073`, `22`, `00`, `state:22`, `msa:35380`, `cluster:<id>`.

**`meta.geography.region.areas[].kind` "nation" | "state" | "county" | "stateAll" | "msa" | "cluster" always present**

What the token is: the nation (`00`), a state's own row (`22`), a county, `stateAll` (a `state:XX` token: every county of the state, its catch-all included), an MSA, or one of the organization's area clusters.

**`meta.geography.region.areas[].code` string always present**

The area's code: a FIPS code for a county or state (the 2-digit state code for `stateAll`), or the token itself, lower-cased, for `msa:<code>` and `cluster:<id>`.

**`meta.geography.region.areas[].name` string or null always 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[].resolved` array of string always present**

The area codes this token contributed to the sum after entitlement. A county the plan does not cover appears in no list; `geography.excluded` counts it.

**`meta.geography.region.resolved` array of string always present**

Every area code summed, deduplicated, in request order, after entitlement.

**`meta.pagination` DataPagination always present**

**`meta.pagination.cursor` string or null always present**

The `cursor` this page was requested with; null for the first page or an unpaged request.

**`meta.pagination.next_cursor` string or null always present**

Pass as `cursor` to get the next page. Null on the last page and on every unpaged response, so null always means there is nothing more.

**`meta.pagination.count` integer always present**

Rows in `data` on this response: series on /series and /lq, rows on /map, items on a taxonomy. (at least 0)

#### `304` redirect

Not modified: the `If-None-Match` tag still matches. No body.

**`ETag` header**

The current tag.

#### `400` error

Bad request. Codes: [`unknown_parameter`](/docs/api/errors#code-unknown-parameter) (a query parameter this route does not take; `parameters[]` names them), [`repeated_parameter`](/docs/api/errors#code-repeated-parameter) (a parameter given more than once; `parameter`), [`bad_request`](/docs/api/errors#code-bad-request) (more than 50 query parameters), [`bad_request`](/docs/api/errors#code-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`](/docs/api/errors#code-bad-request) for a `level` that is not an integer from 0 to 2 or a `parent` that is not `00`, a 2-digit state or a 5-digit county, [`bad_request`](/docs/api/errors#code-bad-request) for a bad `cursor` or a `limit` outside 1 to 5,000.

As `application/problem+json`.

#### `401` error

Unauthenticated. Codes: [`unauthenticated`](/docs/api/errors#code-unauthenticated) (no credential, or an API key or session that is unknown or expired); [`key_revoked`](/docs/api/errors#code-key-revoked) (an API key that was revoked: `revoked_at`, and `reason` -- `holder`, `admin`, `member_removed`, `seat_removed`, `api_disabled`, `addon_ended`, [`account_closed`](/docs/api/errors#code-account-closed), [`org_closed`](/docs/api/errors#code-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`](/docs/api/errors#code-no-membership) (the account has no active organization membership), [`account_suspended`](/docs/api/errors#code-account-suspended) (O56: the caller's own account is suspended by DTG; `reason` where recorded), [`org_suspended`](/docs/api/errors#code-org-suspended) (the organization is suspended; `organization` {id, name} and, where recorded, `reason`), [`org_closed`](/docs/api/errors#code-org-closed) (the organization is closed; `organization`), [`wrong_instance`](/docs/api/errors#code-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).

As `application/problem+json`.

#### `404` error

Not found. Codes: [`not_found`](/docs/api/errors#code-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`](/docs/api/errors#code-vintage-superseded) (the pinned vintage was withdrawn and replaced by a correction; `superseded_by` names it; re-run against it), [`vintage_withdrawn`](/docs/api/errors#code-vintage-withdrawn) (the pinned vintage was withdrawn and no correction exists yet; `current` names the vintage to use).

As `application/problem+json`.

#### `414` error

URI too long. Codes: [`uri_too_long`](/docs/api/errors#code-uri-too-long) (the request URL is longer than the configured maximum, 8,192 bytes by default).

As `application/problem+json`.

#### `429` error

Rate limited, and only that: [`rate_limited`](/docs/api/errors#code-rate-limited) (over the 120 requests a minute per credential or 300 a minute per organization; the sandbox ceilings are lower). `Retry-After` and `X-RateLimit-*` say when to retry. This route is not admission-gated or metered.

As `application/problem+json`.

**`Retry-After` header**

Seconds to wait before retrying.

**`X-RateLimit-Limit` header**

The per-minute request ceiling that applies.

**`X-RateLimit-Remaining` header**

Requests left in the current window.

**`X-RateLimit-Reset` header**

Seconds until the window refills: on every answer, the time until the bucket is full again; on a refusal, until a request is allowed (as `Retry-After`).

#### `500` error

Server error. Codes: [`internal_error`](/docs/api/errors#code-internal-error) (for example a vintage file the server cannot read).

As `application/problem+json`.

Problem codes: [`unknown_parameter`](/docs/api/errors#code-unknown-parameter) (400), [`repeated_parameter`](/docs/api/errors#code-repeated-parameter) (400), [`bad_request`](/docs/api/errors#code-bad-request) (400), [`unauthenticated`](/docs/api/errors#code-unauthenticated) (401), [`key_revoked`](/docs/api/errors#code-key-revoked) (401), [`account_closed`](/docs/api/errors#code-account-closed) (401), [`org_closed`](/docs/api/errors#code-org-closed) (401), [`no_membership`](/docs/api/errors#code-no-membership) (403), [`account_suspended`](/docs/api/errors#code-account-suspended) (403), [`org_suspended`](/docs/api/errors#code-org-suspended) (403), [`org_closed`](/docs/api/errors#code-org-closed) (403), [`wrong_instance`](/docs/api/errors#code-wrong-instance) (403), [`not_found`](/docs/api/errors#code-not-found) (404), [`vintage_superseded`](/docs/api/errors#code-vintage-superseded) (404), [`vintage_withdrawn`](/docs/api/errors#code-vintage-withdrawn) (404), [`uri_too_long`](/docs/api/errors#code-uri-too-long) (414), [`rate_limited`](/docs/api/errors#code-rate-limited) (429), [`internal_error`](/docs/api/errors#code-internal-error) (500). Every problem is a [problem document](/docs/api/errors).

## GET `/v2/taxonomies/msas`

**Metropolitan statistical areas, live and retired**

Credentials: API key or App session. Operation id `dataListMsas`.

Every MSA the vintage's taxonomy names, in code order, with its member counties at the latest MSA map year. Retired MSAs (no counties in the latest map) are listed too, marked, with where their counties went, so a retired name stays findable. Filters combine (AND). Any authenticated caller: an API key (live or sandbox) or a session, any role. Taxonomies are free: not behind the entitlement gate, not admission-gated, not metered, and not limited by plan (a non-current `vintage=` needs no prior-vintage entitlement here; a withdrawn or superseded one is still refused). A sandbox key reads the sandbox vintage's taxonomy, which is real apart from its five invented counties and MSAs. Caching: with an explicit `vintage=` the answer can never change and is `Cache-Control: public, max-age=31536000, immutable`; without it, `no-cache` with the same strong `ETag`, so revalidating with `If-None-Match` answers an empty 304 until a release moves the current vintage. Problems are never cached.

### Parameters

**`q` query · string**

Search: MSAs whose title contains this text (case-insensitive) or whose code starts with it.

**`contains_fips` query · string**

Only the MSA this county belongs to NOW (matched against `member_fips`, so a retired MSA is never returned). (pattern ^\\d{5}$)

**`cursor` query · string**

Opaque cursor from `meta.pagination.next_cursor` of the previous page.

**`limit` query · integer**

Items per page, 1 to 5,000 (default 1,000). (default 1000; 1 to 5000)

**`vintage` query · 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+)$)

**`X-DTG-Org` header · 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`](/docs/api/errors#code-not-found).

### Responses

#### `200` success

A page of MSAs.

As `application/json`.

**`X-RateLimit-Limit` header**

The per-minute request ceiling that applies.

**`X-RateLimit-Remaining` header**

Requests left in the current window.

**`X-RateLimit-Reset` header**

Seconds until the window refills: on every answer, the time until the bucket is full again; on a refusal, until a request is allowed (as `Retry-After`).

**`X-DTG-Sandbox` header**

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

**`ETag` header**

A strong tag over the body less `meta.generated_at`: the same for a pinned and an unpinned read of the same content and for every credential.

**`Cache-Control` header**

`public, max-age=31536000, immutable` when `vintage=` was given; `no-cache` otherwise.

**`data` array of DataMsa always present**

The MSAs on this page, in code order.

**`data[].code` string always present**

The MSA code, without leading zeros (`35380`); use it as `areas=msa:<code>`.

**`data[].title` string or null always present**

The MSA's name.

**`data[].member_fips` array of string always present**

The member county codes in the LATEST MSA map year; empty for a retired MSA. The only field `contains_fips` matches. `msa:<code>` expands to these, catch-alls never included.

**`data[].retired` boolean always present**

True when the MSA has no counties in the latest map. A retired MSA named in `msa:<code>` is 400 [`retired_msa`](/docs/api/errors#code-retired-msa); it is never silently replaced by its successor.

**`data[].retired_after` integer**

Retired only: the last MSA map year in which it had member counties.

**`data[].last_member_fips` array of string**

Retired only: its member counties in that year.

**`data[].successors` array of object**

Retired only: the MSAs those counties belong to in the latest map, each with how many of them; ordered by that count descending, then code. Empty when none of them is in any MSA now.

**`data[].successors[].code` string always present**

The successor MSA's code.

**`data[].successors[].title` string or null always present**

The successor MSA's name.

**`data[].successors[].counties` integer always present**

How many of the retired MSA's last member counties are in this MSA now. (at least 1)

**`meta` DataMeta always present**

The `meta` of every v2 envelope (spec section 3). It is written after `data` on the wire. Clients must tolerate unknown fields.

**`meta.vintage` string always present**

The vintage the answer was read from: the internal build id (`20260913`; a sandbox key's are `sandbox-2026.1`). Cite it and pass it back as `vintage=` to reproduce the answer exactly: a released vintage never changes. Its public name, such as `DTG 2026.3`, is the `label` on `GET /v2/vintages`.

**`meta.sandbox` true**

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_at` string (date-time) always present**

When this response was produced, UTC, to the second (`2026-09-27T18:42:15Z`).

**`meta.granularity` "annual" | "quarterly"**

Data routes only: the period granularity of the points (on /map, `quarterly` when `qtr` was given). Absent on taxonomy responses.

**`meta.provenance_boundaries` DataProvenanceBoundaries always present**

Where the vintage's published record ends and estimation begins, recorded per vintage at build time and returned on every envelope. Compare a point's period against these ranges to know what kind of value it is. For OCCUPATION data this is the only provenance there is: occupation values carry no per-value label, so a period inside `actuals` is published-basis, inside `ces` carried forward, inside `projections` projected. Industry values also carry their own `disclosure` code, which is authoritative for the value. The ranges move with each release; read them, never hard-code them.

**`meta.provenance_boundaries.actuals` object always 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.from` string always present**

First quarter of the range, lower case, e.g. `2004q1`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.actuals.to` string always present**

Last quarter of the range, lower case, e.g. `2025q4`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.ces` object always 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.from` string always present**

First quarter of the range, lower case, e.g. `2004q1`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.ces.to` string always present**

Last quarter of the range, lower case, e.g. `2025q4`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.projections` object always present**

Projected quarters, out to the end of the period axis. Industry values here have disclosure 3.

**`meta.provenance_boundaries.projections.from` string always present**

First quarter of the range, lower case, e.g. `2004q1`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.projections.to` string always present**

Last quarter of the range, lower case, e.g. `2025q4`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.geography` object**

Data routes only. Absent on taxonomy responses.

**`meta.geography.requested` integer always present**

/series and /lq: the number of distinct areas the request resolved to before entitlement (a `state:XX`, `msa:` or `cluster:` token counts its counties; a plain state code counts 1; no `areas` counts the nation as 1). /map: the counties of the `areas` restriction, or without one the counties with a row for this code and period (catch-alls never counted). (at least 0)

**`meta.geography.returned` integer always 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.excluded` integer always present**

Requested counties the organization's entitlement removed (outside the active county set, or every county while the subscription is past due). On /map a requested-versus-returned gap beyond `excluded` means counties with no data for the code and period, not an exclusion. (at least 0)

**`meta.geography.region` DataRegion**

Present only on a request whose areas were summed into one series (a lone `cluster:<id>` or `msa:<code>`, or `sum=areas` / `combine=sum`): what the summed area is made of. The CSV preamble prints it as `# region:` lines and the workbook's Source sheet as `Region` rows.

**`meta.geography.region.label` string always present**

The summed series' `area` label: the cluster's name or the MSA's title when one was asked for alone, otherwise `custom region`.

**`meta.geography.region.areas` array of DataRegionArea always present**

Each area as requested, in request order.

**`meta.geography.region.areas[].token` string always present**

The token exactly as requested: `22073`, `22`, `00`, `state:22`, `msa:35380`, `cluster:<id>`.

**`meta.geography.region.areas[].kind` "nation" | "state" | "county" | "stateAll" | "msa" | "cluster" always present**

What the token is: the nation (`00`), a state's own row (`22`), a county, `stateAll` (a `state:XX` token: every county of the state, its catch-all included), an MSA, or one of the organization's area clusters.

**`meta.geography.region.areas[].code` string always present**

The area's code: a FIPS code for a county or state (the 2-digit state code for `stateAll`), or the token itself, lower-cased, for `msa:<code>` and `cluster:<id>`.

**`meta.geography.region.areas[].name` string or null always 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[].resolved` array of string always present**

The area codes this token contributed to the sum after entitlement. A county the plan does not cover appears in no list; `geography.excluded` counts it.

**`meta.geography.region.resolved` array of string always present**

Every area code summed, deduplicated, in request order, after entitlement.

**`meta.pagination` DataPagination always present**

**`meta.pagination.cursor` string or null always present**

The `cursor` this page was requested with; null for the first page or an unpaged request.

**`meta.pagination.next_cursor` string or null always present**

Pass as `cursor` to get the next page. Null on the last page and on every unpaged response, so null always means there is nothing more.

**`meta.pagination.count` integer always present**

Rows in `data` on this response: series on /series and /lq, rows on /map, items on a taxonomy. (at least 0)

#### `304` redirect

Not modified: the `If-None-Match` tag still matches. No body.

**`ETag` header**

The current tag.

#### `400` error

Bad request. Codes: [`unknown_parameter`](/docs/api/errors#code-unknown-parameter) (a query parameter this route does not take; `parameters[]` names them), [`repeated_parameter`](/docs/api/errors#code-repeated-parameter) (a parameter given more than once; `parameter`), [`bad_request`](/docs/api/errors#code-bad-request) (more than 50 query parameters), [`bad_request`](/docs/api/errors#code-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`](/docs/api/errors#code-bad-request) for a `contains_fips` that is not a 5-digit county code, [`bad_request`](/docs/api/errors#code-bad-request) for a bad `cursor` or a `limit` outside 1 to 5,000.

As `application/problem+json`.

#### `401` error

Unauthenticated. Codes: [`unauthenticated`](/docs/api/errors#code-unauthenticated) (no credential, or an API key or session that is unknown or expired); [`key_revoked`](/docs/api/errors#code-key-revoked) (an API key that was revoked: `revoked_at`, and `reason` -- `holder`, `admin`, `member_removed`, `seat_removed`, `api_disabled`, `addon_ended`, [`account_closed`](/docs/api/errors#code-account-closed), [`org_closed`](/docs/api/errors#code-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`](/docs/api/errors#code-no-membership) (the account has no active organization membership), [`account_suspended`](/docs/api/errors#code-account-suspended) (O56: the caller's own account is suspended by DTG; `reason` where recorded), [`org_suspended`](/docs/api/errors#code-org-suspended) (the organization is suspended; `organization` {id, name} and, where recorded, `reason`), [`org_closed`](/docs/api/errors#code-org-closed) (the organization is closed; `organization`), [`wrong_instance`](/docs/api/errors#code-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).

As `application/problem+json`.

#### `404` error

Not found. Codes: [`not_found`](/docs/api/errors#code-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`](/docs/api/errors#code-vintage-superseded) (the pinned vintage was withdrawn and replaced by a correction; `superseded_by` names it; re-run against it), [`vintage_withdrawn`](/docs/api/errors#code-vintage-withdrawn) (the pinned vintage was withdrawn and no correction exists yet; `current` names the vintage to use).

As `application/problem+json`.

#### `414` error

URI too long. Codes: [`uri_too_long`](/docs/api/errors#code-uri-too-long) (the request URL is longer than the configured maximum, 8,192 bytes by default).

As `application/problem+json`.

#### `429` error

Rate limited, and only that: [`rate_limited`](/docs/api/errors#code-rate-limited) (over the 120 requests a minute per credential or 300 a minute per organization; the sandbox ceilings are lower). `Retry-After` and `X-RateLimit-*` say when to retry. This route is not admission-gated or metered.

As `application/problem+json`.

**`Retry-After` header**

Seconds to wait before retrying.

**`X-RateLimit-Limit` header**

The per-minute request ceiling that applies.

**`X-RateLimit-Remaining` header**

Requests left in the current window.

**`X-RateLimit-Reset` header**

Seconds until the window refills: on every answer, the time until the bucket is full again; on a refusal, until a request is allowed (as `Retry-After`).

#### `500` error

Server error. Codes: [`internal_error`](/docs/api/errors#code-internal-error) (for example a vintage file the server cannot read).

As `application/problem+json`.

Problem codes: [`unknown_parameter`](/docs/api/errors#code-unknown-parameter) (400), [`repeated_parameter`](/docs/api/errors#code-repeated-parameter) (400), [`bad_request`](/docs/api/errors#code-bad-request) (400), [`unauthenticated`](/docs/api/errors#code-unauthenticated) (401), [`key_revoked`](/docs/api/errors#code-key-revoked) (401), [`account_closed`](/docs/api/errors#code-account-closed) (401), [`org_closed`](/docs/api/errors#code-org-closed) (401), [`no_membership`](/docs/api/errors#code-no-membership) (403), [`account_suspended`](/docs/api/errors#code-account-suspended) (403), [`org_suspended`](/docs/api/errors#code-org-suspended) (403), [`org_closed`](/docs/api/errors#code-org-closed) (403), [`wrong_instance`](/docs/api/errors#code-wrong-instance) (403), [`not_found`](/docs/api/errors#code-not-found) (404), [`vintage_superseded`](/docs/api/errors#code-vintage-superseded) (404), [`vintage_withdrawn`](/docs/api/errors#code-vintage-withdrawn) (404), [`uri_too_long`](/docs/api/errors#code-uri-too-long) (414), [`rate_limited`](/docs/api/errors#code-rate-limited) (429), [`internal_error`](/docs/api/errors#code-internal-error) (500). Every problem is a [problem document](/docs/api/errors).

## GET `/v2/taxonomies/ownership`

**Ownership codes**

Credentials: API key or App session. Operation id `dataListOwnership`.

The four ownerships the data is split by (federal, state and local government, private), with the tokens `ownership=` takes. Never paged. Any authenticated caller: an API key (live or sandbox) or a session, any role. Taxonomies are free: not behind the entitlement gate, not admission-gated, not metered, and not limited by plan (a non-current `vintage=` needs no prior-vintage entitlement here; a withdrawn or superseded one is still refused). A sandbox key reads the sandbox vintage's taxonomy, which is real apart from its five invented counties and MSAs. Caching: with an explicit `vintage=` the answer can never change and is `Cache-Control: public, max-age=31536000, immutable`; without it, `no-cache` with the same strong `ETag`, so revalidating with `If-None-Match` answers an empty 304 until a release moves the current vintage. Problems are never cached.

### Parameters

**`vintage` query · 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+)$)

**`X-DTG-Org` header · 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`](/docs/api/errors#code-not-found).

### Responses

#### `200` success

The ownerships.

As `application/json`.

**`X-RateLimit-Limit` header**

The per-minute request ceiling that applies.

**`X-RateLimit-Remaining` header**

Requests left in the current window.

**`X-RateLimit-Reset` header**

Seconds until the window refills: on every answer, the time until the bucket is full again; on a refusal, until a request is allowed (as `Retry-After`).

**`X-DTG-Sandbox` header**

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

**`ETag` header**

A strong tag over the body less `meta.generated_at`: the same for a pinned and an unpinned read of the same content and for every credential.

**`Cache-Control` header**

`public, max-age=31536000, immutable` when `vintage=` was given; `no-cache` otherwise.

**`data` array of DataOwnershipItem always present**

The ownerships, in QCEW code order.

**`data[].code` "fed" | "state" | "local" | "private" always present**

The token to use in `ownership=`.

**`data[].legacy_code` 1 | 2 | 3 | 5 always present**

The QCEW ownership code (1 federal, 2 state, 3 local government, 5 private), also accepted in `ownership=`.

**`data[].title` string always present**

The ownership's name, e.g. `Federal Government`.

**`meta` DataMeta always present**

The `meta` of every v2 envelope (spec section 3). It is written after `data` on the wire. Clients must tolerate unknown fields.

**`meta.vintage` string always present**

The vintage the answer was read from: the internal build id (`20260913`; a sandbox key's are `sandbox-2026.1`). Cite it and pass it back as `vintage=` to reproduce the answer exactly: a released vintage never changes. Its public name, such as `DTG 2026.3`, is the `label` on `GET /v2/vintages`.

**`meta.sandbox` true**

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_at` string (date-time) always present**

When this response was produced, UTC, to the second (`2026-09-27T18:42:15Z`).

**`meta.granularity` "annual" | "quarterly"**

Data routes only: the period granularity of the points (on /map, `quarterly` when `qtr` was given). Absent on taxonomy responses.

**`meta.provenance_boundaries` DataProvenanceBoundaries always present**

Where the vintage's published record ends and estimation begins, recorded per vintage at build time and returned on every envelope. Compare a point's period against these ranges to know what kind of value it is. For OCCUPATION data this is the only provenance there is: occupation values carry no per-value label, so a period inside `actuals` is published-basis, inside `ces` carried forward, inside `projections` projected. Industry values also carry their own `disclosure` code, which is authoritative for the value. The ranges move with each release; read them, never hard-code them.

**`meta.provenance_boundaries.actuals` object always 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.from` string always present**

First quarter of the range, lower case, e.g. `2004q1`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.actuals.to` string always present**

Last quarter of the range, lower case, e.g. `2025q4`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.ces` object always 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.from` string always present**

First quarter of the range, lower case, e.g. `2004q1`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.ces.to` string always present**

Last quarter of the range, lower case, e.g. `2025q4`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.projections` object always present**

Projected quarters, out to the end of the period axis. Industry values here have disclosure 3.

**`meta.provenance_boundaries.projections.from` string always present**

First quarter of the range, lower case, e.g. `2004q1`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.projections.to` string always present**

Last quarter of the range, lower case, e.g. `2025q4`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.geography` object**

Data routes only. Absent on taxonomy responses.

**`meta.geography.requested` integer always present**

/series and /lq: the number of distinct areas the request resolved to before entitlement (a `state:XX`, `msa:` or `cluster:` token counts its counties; a plain state code counts 1; no `areas` counts the nation as 1). /map: the counties of the `areas` restriction, or without one the counties with a row for this code and period (catch-alls never counted). (at least 0)

**`meta.geography.returned` integer always 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.excluded` integer always present**

Requested counties the organization's entitlement removed (outside the active county set, or every county while the subscription is past due). On /map a requested-versus-returned gap beyond `excluded` means counties with no data for the code and period, not an exclusion. (at least 0)

**`meta.geography.region` DataRegion**

Present only on a request whose areas were summed into one series (a lone `cluster:<id>` or `msa:<code>`, or `sum=areas` / `combine=sum`): what the summed area is made of. The CSV preamble prints it as `# region:` lines and the workbook's Source sheet as `Region` rows.

**`meta.geography.region.label` string always present**

The summed series' `area` label: the cluster's name or the MSA's title when one was asked for alone, otherwise `custom region`.

**`meta.geography.region.areas` array of DataRegionArea always present**

Each area as requested, in request order.

**`meta.geography.region.areas[].token` string always present**

The token exactly as requested: `22073`, `22`, `00`, `state:22`, `msa:35380`, `cluster:<id>`.

**`meta.geography.region.areas[].kind` "nation" | "state" | "county" | "stateAll" | "msa" | "cluster" always present**

What the token is: the nation (`00`), a state's own row (`22`), a county, `stateAll` (a `state:XX` token: every county of the state, its catch-all included), an MSA, or one of the organization's area clusters.

**`meta.geography.region.areas[].code` string always present**

The area's code: a FIPS code for a county or state (the 2-digit state code for `stateAll`), or the token itself, lower-cased, for `msa:<code>` and `cluster:<id>`.

**`meta.geography.region.areas[].name` string or null always 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[].resolved` array of string always present**

The area codes this token contributed to the sum after entitlement. A county the plan does not cover appears in no list; `geography.excluded` counts it.

**`meta.geography.region.resolved` array of string always present**

Every area code summed, deduplicated, in request order, after entitlement.

**`meta.pagination` DataPagination always present**

**`meta.pagination.cursor` string or null always present**

The `cursor` this page was requested with; null for the first page or an unpaged request.

**`meta.pagination.next_cursor` string or null always present**

Pass as `cursor` to get the next page. Null on the last page and on every unpaged response, so null always means there is nothing more.

**`meta.pagination.count` integer always present**

Rows in `data` on this response: series on /series and /lq, rows on /map, items on a taxonomy. (at least 0)

#### `304` redirect

Not modified: the `If-None-Match` tag still matches. No body.

**`ETag` header**

The current tag.

#### `400` error

Bad request. Codes: [`unknown_parameter`](/docs/api/errors#code-unknown-parameter) (a query parameter this route does not take; `parameters[]` names them), [`repeated_parameter`](/docs/api/errors#code-repeated-parameter) (a parameter given more than once; `parameter`), [`bad_request`](/docs/api/errors#code-bad-request) (more than 50 query parameters), [`bad_request`](/docs/api/errors#code-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).

As `application/problem+json`.

#### `401` error

Unauthenticated. Codes: [`unauthenticated`](/docs/api/errors#code-unauthenticated) (no credential, or an API key or session that is unknown or expired); [`key_revoked`](/docs/api/errors#code-key-revoked) (an API key that was revoked: `revoked_at`, and `reason` -- `holder`, `admin`, `member_removed`, `seat_removed`, `api_disabled`, `addon_ended`, [`account_closed`](/docs/api/errors#code-account-closed), [`org_closed`](/docs/api/errors#code-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`](/docs/api/errors#code-no-membership) (the account has no active organization membership), [`account_suspended`](/docs/api/errors#code-account-suspended) (O56: the caller's own account is suspended by DTG; `reason` where recorded), [`org_suspended`](/docs/api/errors#code-org-suspended) (the organization is suspended; `organization` {id, name} and, where recorded, `reason`), [`org_closed`](/docs/api/errors#code-org-closed) (the organization is closed; `organization`), [`wrong_instance`](/docs/api/errors#code-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).

As `application/problem+json`.

#### `404` error

Not found. Codes: [`not_found`](/docs/api/errors#code-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`](/docs/api/errors#code-vintage-superseded) (the pinned vintage was withdrawn and replaced by a correction; `superseded_by` names it; re-run against it), [`vintage_withdrawn`](/docs/api/errors#code-vintage-withdrawn) (the pinned vintage was withdrawn and no correction exists yet; `current` names the vintage to use).

As `application/problem+json`.

#### `414` error

URI too long. Codes: [`uri_too_long`](/docs/api/errors#code-uri-too-long) (the request URL is longer than the configured maximum, 8,192 bytes by default).

As `application/problem+json`.

#### `429` error

Rate limited, and only that: [`rate_limited`](/docs/api/errors#code-rate-limited) (over the 120 requests a minute per credential or 300 a minute per organization; the sandbox ceilings are lower). `Retry-After` and `X-RateLimit-*` say when to retry. This route is not admission-gated or metered.

As `application/problem+json`.

**`Retry-After` header**

Seconds to wait before retrying.

**`X-RateLimit-Limit` header**

The per-minute request ceiling that applies.

**`X-RateLimit-Remaining` header**

Requests left in the current window.

**`X-RateLimit-Reset` header**

Seconds until the window refills: on every answer, the time until the bucket is full again; on a refusal, until a request is allowed (as `Retry-After`).

#### `500` error

Server error. Codes: [`internal_error`](/docs/api/errors#code-internal-error) (for example a vintage file the server cannot read).

As `application/problem+json`.

Problem codes: [`unknown_parameter`](/docs/api/errors#code-unknown-parameter) (400), [`repeated_parameter`](/docs/api/errors#code-repeated-parameter) (400), [`bad_request`](/docs/api/errors#code-bad-request) (400), [`unauthenticated`](/docs/api/errors#code-unauthenticated) (401), [`key_revoked`](/docs/api/errors#code-key-revoked) (401), [`account_closed`](/docs/api/errors#code-account-closed) (401), [`org_closed`](/docs/api/errors#code-org-closed) (401), [`no_membership`](/docs/api/errors#code-no-membership) (403), [`account_suspended`](/docs/api/errors#code-account-suspended) (403), [`org_suspended`](/docs/api/errors#code-org-suspended) (403), [`org_closed`](/docs/api/errors#code-org-closed) (403), [`wrong_instance`](/docs/api/errors#code-wrong-instance) (403), [`not_found`](/docs/api/errors#code-not-found) (404), [`vintage_superseded`](/docs/api/errors#code-vintage-superseded) (404), [`vintage_withdrawn`](/docs/api/errors#code-vintage-withdrawn) (404), [`uri_too_long`](/docs/api/errors#code-uri-too-long) (414), [`rate_limited`](/docs/api/errors#code-rate-limited) (429), [`internal_error`](/docs/api/errors#code-internal-error) (500). Every problem is a [problem document](/docs/api/errors).

## GET `/v2/taxonomies/periods`

**The period axis**

Credentials: API key or App session. Operation id `dataListPeriods`.

Every (year, quarter) the vintage holds, in order, including the carried-forward and projected quarters (`meta.provenance_boundaries` says which). `from` and `to` on the data routes must fall inside it. Never paged. Any authenticated caller: an API key (live or sandbox) or a session, any role. Taxonomies are free: not behind the entitlement gate, not admission-gated, not metered, and not limited by plan (a non-current `vintage=` needs no prior-vintage entitlement here; a withdrawn or superseded one is still refused). A sandbox key reads the sandbox vintage's taxonomy, which is real apart from its five invented counties and MSAs. Caching: with an explicit `vintage=` the answer can never change and is `Cache-Control: public, max-age=31536000, immutable`; without it, `no-cache` with the same strong `ETag`, so revalidating with `If-None-Match` answers an empty 304 until a release moves the current vintage. Problems are never cached.

### Parameters

**`vintage` query · 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+)$)

**`X-DTG-Org` header · 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`](/docs/api/errors#code-not-found).

### Responses

#### `200` success

The quarters.

As `application/json`.

**`X-RateLimit-Limit` header**

The per-minute request ceiling that applies.

**`X-RateLimit-Remaining` header**

Requests left in the current window.

**`X-RateLimit-Reset` header**

Seconds until the window refills: on every answer, the time until the bucket is full again; on a refusal, until a request is allowed (as `Retry-After`).

**`X-DTG-Sandbox` header**

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

**`ETag` header**

A strong tag over the body less `meta.generated_at`: the same for a pinned and an unpinned read of the same content and for every credential.

**`Cache-Control` header**

`public, max-age=31536000, immutable` when `vintage=` was given; `no-cache` otherwise.

**`data` array of DataPeriodItem always present**

Every quarter, in time order.

**`data[].year` integer always present**

Calendar year.

**`data[].quarter` 1 | 2 | 3 | 4 always present**

Calendar quarter.

**`meta` DataMeta always present**

The `meta` of every v2 envelope (spec section 3). It is written after `data` on the wire. Clients must tolerate unknown fields.

**`meta.vintage` string always present**

The vintage the answer was read from: the internal build id (`20260913`; a sandbox key's are `sandbox-2026.1`). Cite it and pass it back as `vintage=` to reproduce the answer exactly: a released vintage never changes. Its public name, such as `DTG 2026.3`, is the `label` on `GET /v2/vintages`.

**`meta.sandbox` true**

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_at` string (date-time) always present**

When this response was produced, UTC, to the second (`2026-09-27T18:42:15Z`).

**`meta.granularity` "annual" | "quarterly"**

Data routes only: the period granularity of the points (on /map, `quarterly` when `qtr` was given). Absent on taxonomy responses.

**`meta.provenance_boundaries` DataProvenanceBoundaries always present**

Where the vintage's published record ends and estimation begins, recorded per vintage at build time and returned on every envelope. Compare a point's period against these ranges to know what kind of value it is. For OCCUPATION data this is the only provenance there is: occupation values carry no per-value label, so a period inside `actuals` is published-basis, inside `ces` carried forward, inside `projections` projected. Industry values also carry their own `disclosure` code, which is authoritative for the value. The ranges move with each release; read them, never hard-code them.

**`meta.provenance_boundaries.actuals` object always 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.from` string always present**

First quarter of the range, lower case, e.g. `2004q1`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.actuals.to` string always present**

Last quarter of the range, lower case, e.g. `2025q4`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.ces` object always 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.from` string always present**

First quarter of the range, lower case, e.g. `2004q1`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.ces.to` string always present**

Last quarter of the range, lower case, e.g. `2025q4`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.projections` object always present**

Projected quarters, out to the end of the period axis. Industry values here have disclosure 3.

**`meta.provenance_boundaries.projections.from` string always present**

First quarter of the range, lower case, e.g. `2004q1`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.provenance_boundaries.projections.to` string always present**

Last quarter of the range, lower case, e.g. `2025q4`. (pattern ^\\d{4}q\[1-4\]$)

**`meta.geography` object**

Data routes only. Absent on taxonomy responses.

**`meta.geography.requested` integer always present**

/series and /lq: the number of distinct areas the request resolved to before entitlement (a `state:XX`, `msa:` or `cluster:` token counts its counties; a plain state code counts 1; no `areas` counts the nation as 1). /map: the counties of the `areas` restriction, or without one the counties with a row for this code and period (catch-alls never counted). (at least 0)

**`meta.geography.returned` integer always 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.excluded` integer always present**

Requested counties the organization's entitlement removed (outside the active county set, or every county while the subscription is past due). On /map a requested-versus-returned gap beyond `excluded` means counties with no data for the code and period, not an exclusion. (at least 0)

**`meta.geography.region` DataRegion**

Present only on a request whose areas were summed into one series (a lone `cluster:<id>` or `msa:<code>`, or `sum=areas` / `combine=sum`): what the summed area is made of. The CSV preamble prints it as `# region:` lines and the workbook's Source sheet as `Region` rows.

**`meta.geography.region.label` string always present**

The summed series' `area` label: the cluster's name or the MSA's title when one was asked for alone, otherwise `custom region`.

**`meta.geography.region.areas` array of DataRegionArea always present**

Each area as requested, in request order.

**`meta.geography.region.areas[].token` string always present**

The token exactly as requested: `22073`, `22`, `00`, `state:22`, `msa:35380`, `cluster:<id>`.

**`meta.geography.region.areas[].kind` "nation" | "state" | "county" | "stateAll" | "msa" | "cluster" always present**

What the token is: the nation (`00`), a state's own row (`22`), a county, `stateAll` (a `state:XX` token: every county of the state, its catch-all included), an MSA, or one of the organization's area clusters.

**`meta.geography.region.areas[].code` string always present**

The area's code: a FIPS code for a county or state (the 2-digit state code for `stateAll`), or the token itself, lower-cased, for `msa:<code>` and `cluster:<id>`.

**`meta.geography.region.areas[].name` string or null always 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[].resolved` array of string always present**

The area codes this token contributed to the sum after entitlement. A county the plan does not cover appears in no list; `geography.excluded` counts it.

**`meta.geography.region.resolved` array of string always present**

Every area code summed, deduplicated, in request order, after entitlement.

**`meta.pagination` DataPagination always present**

**`meta.pagination.cursor` string or null always present**

The `cursor` this page was requested with; null for the first page or an unpaged request.

**`meta.pagination.next_cursor` string or null always present**

Pass as `cursor` to get the next page. Null on the last page and on every unpaged response, so null always means there is nothing more.

**`meta.pagination.count` integer always present**

Rows in `data` on this response: series on /series and /lq, rows on /map, items on a taxonomy. (at least 0)

#### `304` redirect

Not modified: the `If-None-Match` tag still matches. No body.

**`ETag` header**

The current tag.

#### `400` error

Bad request. Codes: [`unknown_parameter`](/docs/api/errors#code-unknown-parameter) (a query parameter this route does not take; `parameters[]` names them), [`repeated_parameter`](/docs/api/errors#code-repeated-parameter) (a parameter given more than once; `parameter`), [`bad_request`](/docs/api/errors#code-bad-request) (more than 50 query parameters), [`bad_request`](/docs/api/errors#code-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).

As `application/problem+json`.

#### `401` error

Unauthenticated. Codes: [`unauthenticated`](/docs/api/errors#code-unauthenticated) (no credential, or an API key or session that is unknown or expired); [`key_revoked`](/docs/api/errors#code-key-revoked) (an API key that was revoked: `revoked_at`, and `reason` -- `holder`, `admin`, `member_removed`, `seat_removed`, `api_disabled`, `addon_ended`, [`account_closed`](/docs/api/errors#code-account-closed), [`org_closed`](/docs/api/errors#code-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`](/docs/api/errors#code-no-membership) (the account has no active organization membership), [`account_suspended`](/docs/api/errors#code-account-suspended) (O56: the caller's own account is suspended by DTG; `reason` where recorded), [`org_suspended`](/docs/api/errors#code-org-suspended) (the organization is suspended; `organization` {id, name} and, where recorded, `reason`), [`org_closed`](/docs/api/errors#code-org-closed) (the organization is closed; `organization`), [`wrong_instance`](/docs/api/errors#code-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).

As `application/problem+json`.

#### `404` error

Not found. Codes: [`not_found`](/docs/api/errors#code-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`](/docs/api/errors#code-vintage-superseded) (the pinned vintage was withdrawn and replaced by a correction; `superseded_by` names it; re-run against it), [`vintage_withdrawn`](/docs/api/errors#code-vintage-withdrawn) (the pinned vintage was withdrawn and no correction exists yet; `current` names the vintage to use).

As `application/problem+json`.

#### `414` error

URI too long. Codes: [`uri_too_long`](/docs/api/errors#code-uri-too-long) (the request URL is longer than the configured maximum, 8,192 bytes by default).

As `application/problem+json`.

#### `429` error

Rate limited, and only that: [`rate_limited`](/docs/api/errors#code-rate-limited) (over the 120 requests a minute per credential or 300 a minute per organization; the sandbox ceilings are lower). `Retry-After` and `X-RateLimit-*` say when to retry. This route is not admission-gated or metered.

As `application/problem+json`.

**`Retry-After` header**

Seconds to wait before retrying.

**`X-RateLimit-Limit` header**

The per-minute request ceiling that applies.

**`X-RateLimit-Remaining` header**

Requests left in the current window.

**`X-RateLimit-Reset` header**

Seconds until the window refills: on every answer, the time until the bucket is full again; on a refusal, until a request is allowed (as `Retry-After`).

#### `500` error

Server error. Codes: [`internal_error`](/docs/api/errors#code-internal-error) (for example a vintage file the server cannot read).

As `application/problem+json`.

Problem codes: [`unknown_parameter`](/docs/api/errors#code-unknown-parameter) (400), [`repeated_parameter`](/docs/api/errors#code-repeated-parameter) (400), [`bad_request`](/docs/api/errors#code-bad-request) (400), [`unauthenticated`](/docs/api/errors#code-unauthenticated) (401), [`key_revoked`](/docs/api/errors#code-key-revoked) (401), [`account_closed`](/docs/api/errors#code-account-closed) (401), [`org_closed`](/docs/api/errors#code-org-closed) (401), [`no_membership`](/docs/api/errors#code-no-membership) (403), [`account_suspended`](/docs/api/errors#code-account-suspended) (403), [`org_suspended`](/docs/api/errors#code-org-suspended) (403), [`org_closed`](/docs/api/errors#code-org-closed) (403), [`wrong_instance`](/docs/api/errors#code-wrong-instance) (403), [`not_found`](/docs/api/errors#code-not-found) (404), [`vintage_superseded`](/docs/api/errors#code-vintage-superseded) (404), [`vintage_withdrawn`](/docs/api/errors#code-vintage-withdrawn) (404), [`uri_too_long`](/docs/api/errors#code-uri-too-long) (414), [`rate_limited`](/docs/api/errors#code-rate-limited) (429), [`internal_error`](/docs/api/errors#code-internal-error) (500). Every problem is a [problem document](/docs/api/errors).

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