---
title: "List every served version of a state session law"
method: GET
path: "/api/v1/us/session-laws/{session_law_id}/versions"
tags: ["US Session Laws"]
---

# List every served version of a state session law

`GET /api/v1/us/session-laws/{session_law_id}/versions`

## Request

- Base URL: `https://api.vaquill.ai`
- URL: `https://api.vaquill.ai/api/v1/us/session-laws/{session_law_id}/versions`
- Auth: one of:
  - HTTP bearer
  - API key in header `X-API-Key`
- TypeScript: <https://skmtc.dev/vaquill/apis/vaquill-developer-api/docs/api/v1/us/session-laws/:session_law_id/versions/get.ts> (a function with typed inputs; its response is checked against the schema)

Every version of one state session law that we served or adopted, newest first, and which of them is
current. A law's text can be corrected after we first serve it (a better extraction of the same
source, a re-collection), so the version you read yesterday may not be the one a plain call returns
today. This route lists each version with what identifies it (when we recorded it, its length, its
hash, how the text was taken) and how its text can be read. It carries no text: read one version with
`GET /us/session-laws/{sessionLawId}/body?version=N`.

**Cost**: 1 credit per call, however many versions the law has. A law with a single version
answers a list of one and is charged: "this law has never changed" is the answer you paid for. Reading
a version's text is a separate call at the body price (6 credits per page), the same as the
current text. A request that fails validation (422) is never charged.
A law that is not found (404) is never charged.
A failure on our side (500, 503) is refunded.

**Edge cases.** A law that is in the registry but has no version row (its current version is not set,
or it was withdrawn or merged into another law) answers `200` with `versions: []`, `count: 0` and a
`note`, and is not charged; it is never the 404 "no law carries this id". One answer lists at most
100 versions, newest first; `truncated: true` says older versions exist that it does not show.

**Id only.** This route takes a `sessionLawId`, not a citation: resolve a citation once with
`GET /us/session-laws/{sessionLawId}` and use the id it returns. A federal id (`SAL_...`) is a 404
(`federal_session_law`), as on `/body`.

**What a version says.** `version` is the number to send as `version` on `/body`. Numbers can have
gaps: a version we recorded and never served or adopted is not listed. Exactly one listed version has
`isCurrent: true`, and `currentVersion` repeats its number: it is the text a plain `/body` call
serves. `recordedAt` dates our copy of the text, not the act (`approvedDate` on the law does that).
`totalChars` and `textSha256` identify the text: two versions with the same hash have the same text.
`citation`, `title` and `approvedDate` are as that version recorded them, so a correction that fixed
a title shows here.

| `textAccess` | Reading that version's text |
| --- | --- |
| `served` | A plain call to `/body?version=N`. |
| `includeUnverified` | The law is held back (`textStatus: withheld`); the text is readable only with `includeUnverified=true`, labelled by `textReliability` and `reasonCode` (it may show deleted or vetoed law as if it were enacted). |
| `none` | There is no text for this version, or the law's text may not be published (a licence that bars use, or text that failed our own validation). |

The law-level `textStatus`, `textReliability` and `reasonCode` are the same fields, with the same
meaning, as on `/body`. `textReliability` is omitted when no text of the law may be read, and
`reasonCode` when the law is served and verified. To check that nothing changed under you, compare
`textSha256` here with the one you read; the cursor rules for an older version are on `/body`.

**Example**

```bash
curl "https://api.vaquill.ai/api/v1/us/session-laws/SSL_MN_2025S1_G_Y2025_C1/versions" \
  -H "Authorization: Bearer $VAQUILL_API_KEY"

# then read the first version's text, one page at the body price
curl "https://api.vaquill.ai/api/v1/us/session-laws/SSL_MN_2025S1_G_Y2025_C1/body?version=1" \
  -H "Authorization: Bearer $VAQUILL_API_KEY"
```

```javascript
const id = "SSL_MN_2025S1_G_Y2025_C1";
const res = await fetch(`https://api.vaquill.ai/api/v1/us/session-laws/${encodeURIComponent(id)}/versions`, {
  headers: { Authorization: `Bearer ${process.env.VAQUILL_API_KEY}` },
});
const { versions, currentVersion } = await res.json();
const older = versions.filter((v) => !v.isCurrent && v.textAccess === "served");
```

**Related endpoints**

- `GET /us/session-laws/{sessionLawId}/body` reads the text of the current version, or of the one you
  name with `version`. It also takes `includeUnverified=true` for a law we hold back.
- `GET /us/session-laws/{sessionLawId}` is the law's record (dates, sources, code sections affected,
  whether its text is held); it always describes the current version.
- `GET /us/session-laws/changes` is the registry's change feed: a `corrected` entry there is how you
  learn that a copy of a law you hold may be stale, and this route is where you see what changed.
- `GET /us/statutes/coverage` says which sessions we hold and how complete each is, under each
  jurisdiction's `sessionLaws` block.
- Federal session laws (the Statutes at Large, `SAL_` ids) are not served here. Read them with
  `GET /us/statutes/section/{actId}`.

**Nulls.** A field of a version with no value (`citation`, `title`, `approvedDate`, `totalChars`,
`textSha256`, `textMethod`, `amendatoryMarkup`, `markupConvention`) is `null`. `textReliability` and
`reasonCode` on the law are absent, not null, when they do not apply. The examples here leave the
nulls out.

**Rate limits.** Calls count against your key's per-minute budget like every Data API route; a 429
carries `Retry-After`.

## Path parameters

- `session_law_id` string, required: A state session law's id, for example `SSL_MN_2025S1_G_Y2025_C1`, from a `/us/session-laws/list` result. A citation also works here on `GET /us/session-laws/{sessionLawId}`, and `resolvedFrom` says what your input matched. Name the state in the citation: `Minn. Laws 2025, ch. 1`, `2025 Minn. Laws ch. 1`, `Ch. 1, Laws of Minnesota 2025`, `Ch. 2025-12, Laws of Fla.`, `2025 Wash. Sess. Laws ch. 12`, `La. Acts 2025, No. 12`, `Pa. Act 12 of 2025`, `Minn. S.F. 1552 (2025)`, `MN: Laws 2025, ch. 1`. A form only one state uses needs no state: `P.A. 104-0001` (Illinois), `Pub. Act No. 26-151` (Connecticut), `S.L. 2025-12` (North Carolina), `Stats. 2025, ch. 12` (California), `D.C. Law 25-713`. The publisher's printed form (`CHAPTER 2025-12`, `S.F.No. 1552`) and the `citation` we print on every law also work. A form several states share (`Laws 2025, ch. 1`, `HB 123`, `Act 12 of 2025`) is a 404 with `reason: needs_jurisdiction`, the states it exists in and one candidate per state: we never guess the state. A citation naming several laws of one state (Minnesota 2025 has two Chapter 1s) is a 404 listing all of them, `candidateCount` and `truncated` saying whether the list is complete, and `defaultSessionLawId` the regular session's law where there is exactly one conventional reading. An id or citation that matches nothing in a session we have only partly collected is a 404 with `reason: session_not_fully_collected` and that session's coverage: the law may exist and not be held yet, so it is never charged, the resolve price included. A citation costs the route's price PLUS the `/us/statutes/resolve` price (2 credits), the second kept whether or not it resolves (an unresolved or ambiguous citation is a charged verdict) and refunded only on a partly collected session, on a citation we could not read as a state session-law citation at all (its detail lists the accepted forms) or on a failure. URL-encode the citation; one containing `/` cannot travel in a path segment. Federal session laws (`SAL_...`, `Pub. L. 118-5`, the Statutes at Large) are served by `/us/statutes/section`, not here, and code citations (`26 U.S.C. § 1`) by `/us/statutes/resolve`.

## Headers

- `Idempotency-Key` string, nullable: Optional. 16 to 255 visible ASCII characters (a UUIDv4 works). Repeating the same request with the same key replays the original 2xx response for 24 hours without charging again. The same key on a different request is rejected. With the account's `cacheQueryDerivedData` retention setting off, no body is stored and a retry answers 409 `idempotency_replay_unavailable`, still without a second charge.

## Response `200`

The law's versions, newest first, exactly one of them current. A law with a single version answers a list of one, which is charged.

- SessionLawVersionsResponse: Response for `GET /us/session-laws/{sessionLawId}/versions`.
  - `processingTimeMs` number: Server-side time for this request in milliseconds, excluding network transit. Not billed on.
  - `creditsConsumed` integer: Credits actually charged for this call, never the list price. It includes the `/us/statutes/resolve` fee when a citation was sent where an id goes. 0 when the call was not charged or was refunded (a body that answers `available: false`).
  - `sessionLawId` string, required: The law these versions belong to.
  - `textStatus` 'held' | 'withheld' | 'not_held' | 'pending', required: Whether we serve this law's text. `held`: the text is served by `/us/session-laws/{sessionLawId}/body`. `withheld`: we hold the text but a measured defect keeps it from being served, and `withheldReason` says what. `not_held`: we know the act exists and do not hold its text. `pending`: collected and not yet verified.
  - `textReliability` 'verified' | 'unverified' | 'known_issue', nullable: How far to trust the CURRENT text of this law, the same field as on `/body`: `verified` when the text passed our strike-through check, `unverified` when that check has not been completed, `known_issue` when we have evidence the text shows deleted law as if enacted. A served law can be any of the three (its text is still served); a held-back law is `unverified` or `known_issue` and its text is read with `includeUnverified=true`. Omitted when no text of this law may be read.
  - `reasonCode` string, nullable: Why the text is not `verified`, the same token as on `/body`. Omitted when the text is `verified`.
  - `versions` SessionLawVersion[]: Every version we served or adopted, newest first, at most 100 of them (see `truncated`). At least the current one, except when `note` says the registry lists none.
    - `version` integer, required: The version number, 1 for the first we recorded. Send it as `version` on `GET /us/session-laws/{sessionLawId}/body`. Numbers can have gaps: a version we recorded and never served or adopted is not listed.
    - `isCurrent` boolean, required: True for the version `GET /us/session-laws/{sessionLawId}` and a plain `/body` call serve now. Exactly one listed version is current.
    - `recordedAt` string, required: When we recorded this version, ISO 8601 in UTC. Not an approval or effective date of the act: it dates our copy of the text.
    - `citation` string, nullable: The act's citation as the publisher printed it in this version. Null when none was recorded.
    - `title` string, nullable: The act's title in this version. Null when none was recorded.
    - `approvedDate` string, date, nullable: Approval date recorded in this version, ISO 8601. Null when none was recorded.
    - `totalChars` integer, nullable: Length of this version's text in characters. Null when this version has no text.
    - `textSha256` string, nullable: SHA-256 (hex) of the full text of this law. It changes only when the text does, so it is the cheap way to tell whether a copy you hold is current. Body pages carry the same value. Two versions with the same hash have the same text: only the record around it changed.
    - `textMethod` string, nullable: How the text of this version was taken from the source: for example `pdf_text_layer`, `html`, `xml`. Null when this version has no text.
    - `amendatoryMarkup` string, nullable: Whether the language an amending act strikes and inserts survives in the text. `preserved`: it is in `text`, in the convention `markupConvention` names. `lost`: the publisher printed it and our text does not keep it, so deleted words can read as law: check the source before quoting. `absent`: the act carries no such markup that we found. `not_applicable`: there is no text to carry it.
    - `markupConvention` string, nullable: How preserved markup is written, set only when `amendatoryMarkup` is `preserved`. `wdiff`: deleted language is `[-like this-]` and inserted language `{+like this+}`. `publisher_literal`: the publisher's own words are left as printed (Minnesota prints `new text begin` / `new text end` and `deleted text begin` / `deleted text end` markers). `mixed`: both in one text.
    - `textAccess` 'served' | 'includeUnverified' | 'none', required: How this version's text can be read with `GET /us/session-laws/{sessionLawId}/body?version=N`. `served`: the law is served and this version has text, so a plain call reads it. `includeUnverified`: the law is held back (`textStatus: withheld`) and this text is readable only with `includeUnverified=true`, labelled by `textReliability`. `none`: there is no text to read for this version, or the law's text may not be published.
  - `count` integer, required: Number of versions listed.
  - `currentVersion` integer, nullable: The `version` that is current. Null only when no version is current, which a served law never is.
  - `truncated` boolean: True when the law has more versions than one answer lists, so `versions` is the newest ones and older versions exist that are not shown. False when `versions` is the whole history.
  - `note` string, nullable: Set only when the law exists but the registry lists no version of its text for it (`versions` is empty, `count` is 0): the answer is free, nothing is charged. Omitted otherwise.

## Other responses

- `401`: Missing, invalid or revoked API key. Not charged.
- `402`: Insufficient credits for this call. Nothing was done and nothing was charged. Read your balance, free, at `GET /credits/balance`.
- `403`: The API key lacks the `research:read` scope. Not charged.
- `404`: Unknown id, a federal act, or an endpoint name where the id goes. `reason` says which. Never charged.
- `409`: Idempotency conflict. `idempotency_in_progress`: the original request with this key is still running (retry after `Retry-After`). `idempotency_replay_unavailable`: it completed but its response was too large to replay; nothing is charged again, so check the credit balance instead of retrying.
- `422`: A parameter failed validation: `sessionLawId` empty or over 300 characters. Never charged. `errors[].loc` names the parameter exactly as you send it. `idempotency_key_reused`: the `Idempotency-Key` was already used with a different request.
- `429`: Rate limit exceeded for your key. Not charged. Wait `Retry-After` seconds.
- `500`: An unexpected failure on our side. The charge is refunded.
- `503`: The session-laws database is temporarily unavailable. The charge is refunded; retry.

## Changes

- **2026-11-07** `340c0d24d0b6`: 74 info
  - added the optional property `candidateCount` to the response with the `401` status
  - added the optional property `candidateCount` to the response with the `402` status
  - added the optional property `candidateCount` to the response with the `403` status
  - added the optional property `candidateCount` to the response with the `409` status
  - …70 more

[Operation history](https://skmtc.dev/vaquill/apis/vaquill-developer-api/changes/api/v1/us/session-laws/:session_law_id/versions/get.md)

---

[API](https://skmtc.dev/vaquill/apis/vaquill-developer-api.md) - [All operations](https://skmtc.dev/vaquill/apis/vaquill-developer-api/llms.txt) - [OpenAPI document](https://skmtc.dev/vaquill/apis/vaquill-developer-api/revisions/340c0d24d0b6?raw)
