US Session Laws

Feed of changes to the session-law registry

A sync feed of the registry: every state law that began to be served, had its text added, was corrected, unserved, withdrawn or merged, oldest first, as a cursor over an integer id. Poll it to keep a copy of the registry current without re-listing it: read from sinceId=0 once, then call again with the cursor you were given.

Cost: 1 credit per page of up to 200 entries (default 50). An empty page is an answer, "nothing changed since your cursor", and is charged. A request that fails validation (422) is never charged; a failure on our side (500, 503) is refunded.

Walking the feed. Send sinceId=0 for the first page. While hasMore is true, call again with sinceId set to cursor: the feed is ordered by id, id only grows, and a page carries the entries after sinceId and nothing else, so no entry is skipped or repeated between your calls. When hasMore is false you have caught up as of this call; keep your cursor and ask again later. An empty page has cursor: null: keep the one you hold.

What an entry says. kind is what happened (see its description), sessionLawId the law, jurisdiction its state, and detectedAt when we recorded it, an upper bound on when the law changed in the world and never an approval or effective date. Read the law itself at GET /us/session-laws/{sessionLawId}, or many at once with POST /us/session-laws/batch. A withdrawn or merged entry is a tombstone: the law no longer answers there.

Filter. jurisdiction limits the feed to one state, dc or pr, and then hasMore means more MATCHING entries follow. The cursor is a position in the whole feed, so the same sinceId means the same point with or without the filter, and a filtered page can skip ids.

Read a short window behind your cursor. id is assigned in order but a row can become visible slightly after a later one if two writers commit at once. Every writer today commits a whole batch together, so this has not happened, but a job that must never miss an entry should re-read the last few hundred ids it has already seen.

Scope. State session laws only: federal acts (SAL_...) are never in this feed.

Example

curl "https://api.vaquill.ai/api/v1/us/session-laws/changes?sinceId=0&limit=100" \
  -H "Authorization: Bearer $VAQUILL_API_KEY"
let cursor = 0;
for (;;) {
  const res = await fetch(`https://api.vaquill.ai/api/v1/us/session-laws/changes?sinceId=${cursor}`, {
    headers: { Authorization: `Bearer ${process.env.VAQUILL_API_KEY}` },
  });
  const page = await res.json();
  for (const change of page.changes) handle(change);
  if (page.cursor !== null) cursor = page.cursor;
  if (!page.hasMore) break;
}

Related endpoints

  • GET /us/session-laws/list lists the laws of one state, filtered; this feed is how you learn that one changed.
  • POST /us/session-laws/batch reads many laws at once from the ids this feed returns.
  • GET /us/statutes/coverage says which sessions we hold and how complete each is, in each jurisdiction's sessionLaws block.

Nulls. Every field of a response is always present: one with no value is null (or [] for a list). The examples on this page leave the nulls out for brevity.

get/api/v1/us/session-laws/changes

Request

Query parameters

sinceIdinteger

Return only entries with an id greater than this: the cursor for walking the feed forward. Send 0 (the default) to start from the beginning, then the cursor of your last page. id is an integer that only grows, so this is exact and immune to clock skew. A negative or non-integer value is a 422 and is never charged; a value past the newest id is an empty page, which is charged.

Example:1500
jurisdiction'al' | 'ak' | 'as' | 'az' | 'ar' | 'ca' | 'co' | 'ct' | 'de' | 'dc' | 'fl' | 'ga' | 'gu' | 'hi' | 'id' | 'il' | 'in' | 'ia' | 'ks' | 'ky' | 'la' | 'me' | 'md' | 'ma' | 'mi' | 'mn' | 'ms' | 'mo' | 'mt' | 'ne' | 'nv' | 'nh' | 'nj' | 'nm' | 'ny' | 'nc' | 'nd' | 'mp' | 'oh' | 'ok' | 'or' | 'pa' | 'pr' | 'ri' | 'sc' | 'sd' | 'tn' | 'tx' | 'ut' | 'vt' | 'vi' | 'va' | 'wa' | 'wv' | 'wi' | 'wy' nullable

Limit the feed to one jurisdiction: a two-letter state code, dc or pr, case-insensitive. Omit for every jurisdiction. federal and any unknown code is a 422 and is never charged: federal session laws are not in this feed. The cursor is a position in the whole feed, so a filtered page may have gaps in id.

Example:mn
limitinteger

Most entries on this page: 1 to 200, default 50. The price is per page, however many entries it holds, so a larger page is cheaper per entry. Out of range is a 422 and is never charged.

Example:100

Headers

Idempotency-Keystring nullable

Optional. 16 to 255 visible ASCII characters (a UUIDv4 is ideal). A retry with the same key and the same request replays the original 2xx response for 24 hours without charging again. The same key with a different request is rejected. Scope is the API key, method, path and body. The stored response body is a query-derived cache controlled by the account's data retention setting cacheQueryDerivedData: when it is false the body is never stored and a retry answers 409 idempotency_replay_unavailable, without charging again.

Example:3f9c2a1e-7b44-4d0a-9c1e-5a6b7c8d9e0f

Response

A page of the feed, oldest first. hasMore and cursor say how to continue; an empty changes is a charged answer.

countinteger

Entries on this page.

cursorinteger nullable

The largest id on this page: send it as sinceId for the next page. Null when the page is empty, so keep the cursor you already hold.

hasMoreboolean

True when more entries follow this page: call again with sinceId set to cursor straight away. False means you have caught up as of this call.

creditsConsumedinteger

Credits actually charged for this call, never the list price. 0 when the call was refunded (a failure on our side).

processingTimeMsnumber

Server-side time for this request in milliseconds, excluding network transit. Not billed on.

Example response

{
  "changes": [
    {
      "id": 1501,
      "kind": "served",
      "sessionLawId": "SSL_MN_2025R_G_Y2025_C1",
      "jurisdiction": "mn",
      "detectedAt": "2026-10-05T09:04:11.120118+00:00"
    }
  ],
  "count": 2,
  "cursor": 1502,
  "hasMore": true,
  "creditsConsumed": 1,
  "processingTimeMs": 41.8
}

Changes

No recorded changes to this operation across all 1 revision of this API.