curl --request GET \
--url https://api.vaquill.ai/api/v1/us/session-laws/{session_law_id}/body \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.vaquill.ai/api/v1/us/session-laws/{session_law_id}/body"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.vaquill.ai/api/v1/us/session-laws/{session_law_id}/body', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.vaquill.ai/api/v1/us/session-laws/{session_law_id}/body"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}Get a state session law's text
The act’s text exactly as printed, one page of about 30,000 characters at a
time, cut at a paragraph break where one is near. Follow nextCursor while
hasMore is true; pageStart / pageEnd / totalChars are character offsets
into the whole text, and concatenating the pages in order reproduces it.
Cost: 6 credits per page served. Read estimatedBodyPages on the law first
(charCount divided by about 30,000). A short act is one page; an omnibus act can
be many. Each page is charged on its own, so you can stop whenever you have enough.
pagePrice is what this page cost, totalPages the estimated pages in all, and
estimatedRemainingCredits the pages still to come times the price.
Cost of reading a law. Reading a whole law is estimatedBodyPages times the price
of one body page (6 credits). Both are on the record: charCount is its length
in characters and estimatedBodyPages is that divided by the page size (about
30,000 characters), rounded up, and null unless the text is served. Most acts
take a page or two. The longest we hold, Ohio’s 2025 budget act (HB 96, 9,571,132
characters), takes about 320 pages, 1,920 credits to read in
full, so check estimatedBodyPages before you page through an omnibus act. Each body
response then carries pagePrice (what that page cost) and estimatedRemainingCredits
(the pages still to come times the price) so you can stop whenever you have enough.
Where a page starts. Follow nextCursor from the page before, or send startChar,
a character offset into the text (0-based, the scale of pageStart and totalChars),
to jump straight to a place you know without paging from the start. Send one or the
other, never both (a 422, uncharged). A startChar page costs and sizes the same as any
other, and returns a normal signed nextCursor. startChar has no version binding of
its own, so add expectTextSha256 (the textSha256 you read the text under, in
lowercase hex, 16 characters or more): if the text was re-collected since, the answer is
a 409 and nothing is charged. A startChar at or beyond the end is a 422, refunded.
There is no page-size parameter: a page is always about 30,000 characters, so
its price is fixed.
Attribution. Every outcome carries citation, jurisdiction, session,
approvedDate and sourceUrl, so a page is attributable on its own. sourceUrl is the
official copy on a government host and is present when the text is withheld or not held,
so a reader always has somewhere to go.
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.
Not served, not charged. When we do not serve the text (textStatus is not
held), the answer is a 200 with available: false and a reason (not_held,
withheld, pending or empty_text), creditsConsumed: 0, the
withheldReason for a withheld law, and the official sourceUrl. A federal id (SAL_...) is a 404
(federal_session_law); read federal session laws with
GET /us/statutes/section/{actId}. An unknown id is a 404; a stale cursor or a mismatched expectTextSha256 is a 409
(start again without cursor); a malformed or expired cursor, cursor with startChar,
or a startChar past the end is a 422. None of these is
charged, and a failure on our side (500, 503) is refunded.
Amendatory markup. An amending act prints the language it deletes and inserts.
Where amendatoryMarkup is preserved, that markup is in text in the convention
markupConvention names: wdiff writes deleted language [-like this-] and
inserted language {+like this+}; publisher_literal keeps the publisher’s own
markers as printed (Minnesota’s new text begin and deleted text begin); mixed
has both. Where it is lost, the publisher printed markup our text does not keep,
so deleted words can read as law: check the source before quoting. Only the as
printed text is served; there is no clean “as enacted” view.
Example
curl "https://api.vaquill.ai/api/v1/us/session-laws/SSL_MN_2025S1_G_Y2025_C1/body" \
-H "Authorization: Bearer $VAQUILL_API_KEY"
# jump to a character offset, pinned to the version of the text you read
curl "https://api.vaquill.ai/api/v1/us/session-laws/SSL_MN_2025S1_G_Y2025_C1/body?startChar=29987&expectTextSha256=02693d5b55d4709b" \
-H "Authorization: Bearer $VAQUILL_API_KEY"
const MY_BUDGET = 100; // the most you will spend to finish reading, in credits
let cursor;
let text = "";
do {
const url = new URL("https://api.vaquill.ai/api/v1/us/session-laws/SSL_MN_2025S1_G_Y2025_C1/body");
if (cursor) url.searchParams.set("cursor", cursor);
const res = await fetch(url, {
headers: { Authorization: `Bearer ${process.env.VAQUILL_API_KEY}` },
});
const page = await res.json();
if (!page.available) break; // withheld or not held: page.sourceUrl is the official copy
text += page.text;
if (page.estimatedRemainingCredits > MY_BUDGET) break; // stop whenever you have enough
cursor = page.nextCursor;
} while (cursor);
Related endpoints
GET /us/statutes/resolveanswers a citation that names a state session law rather than a code section with asessionLawblock (matched, orambiguouswith every candidate), pointing here. It never changesresolvedorsection.GET /us/statutes/coveragelists, per jurisdiction undersessionLaws, which sessions we hold session laws for and whether each iscompleteorpartial. Read it first: a law missing from apartialsession is not evidence that it does not exist.- Federal session laws (the Statutes at Large,
SAL_ids) are not served here. Read them withGET /us/statutes/section/{actId}.
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. The
exceptions are blocks that exist only when they apply and are OMITTED otherwise, never
null: publisher, provenance and session.coverageStatus on a law, and coverage on a
list page that has rows.
Rate limits. Calls count against your key’s per-minute budget like every Data
API route; a 429 carries Retry-After.
curl --request GET \
--url https://api.vaquill.ai/api/v1/us/session-laws/{session_law_id}/body \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.vaquill.ai/api/v1/us/session-laws/{session_law_id}/body"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.vaquill.ai/api/v1/us/session-laws/{session_law_id}/body', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.vaquill.ai/api/v1/us/session-laws/{session_law_id}/body"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}Authorizations
API key issued from the developer dashboard. Pass as Authorization: Bearer vq_key_... (preferred).
Path Parameters
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 charged whether or not it resolves. 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.
1 - 300"SSL_MN_2025S1_G_Y2025_C1"
Query Parameters
Where to resume: the previous page's nextCursor, unchanged. Omit for the first page. It is opaque, signed and tied to the exact text it was cut from: a value we did not issue (reason: invalid_cursor), from an older cursor format (cursor_expired) or past the end of the text (cursor_past_end) is a 422, and one from before the text was re-collected is a 409 (start again without it). None is charged. Send either cursor or startChar, never both (a 422, never charged).
600"eyJ2IjoyLCJvIjoyOTk4NywiaCI6IjAyNjkzZDViNTVkNDcwOWIifQ.stWHVSgGqJPIElcTLfAGHA"
Start the page at this character offset into the text (0-based, the same scale as pageStart and totalChars): an alternative to cursor for jumping to a place you already know, such as the character where a section begins, without paging from the start. The page that comes back is the same size and price as any other, its nextCursor is a normal signed cursor you can follow, and a page is still cut at a paragraph break near its end. Send either startChar or cursor, never both (a 422, never charged). 0 to 100,000,000; a value at or beyond the end of the text is a 422 and is refunded. There is no page-size parameter: the price per page is fixed.
0 <= x <= 10000000029987
The textSha256 you read the text under, in lowercase hex (the whole 64 characters, or any prefix of 16 or more). When this law's text no longer has that hash (it was re-collected), the answer is a 409 and nothing is charged, exactly as for a cursor cut from an earlier version: use it with startChar, which otherwise has no version binding of its own. Anything that is not 16 to 64 lowercase hex characters is a 422.
^[0-9a-f]{16,64}$"02693d5b55d4709bdd36fdccee4a9737d2655a3988a90daeb6b7c486ce50260b"
Response
One page of the act's text, or available: false (not charged) when we do not serve it.
Response for GET /us/session-laws/{sessionLawId}/body.
The law this page belongs to.
"SSL_MN_2025S1_G_Y2025_C1"
The act's citation as its publisher prints it, the same as citation on GET /us/session-laws/{sessionLawId}. Carried here so a page is attributable on its own, with no second call.
"CHAPTER 1--S.F.No. 3"
Lowercase two-letter code of the state, dc or pr.
"mn"
The legislative session the law was enacted in.
Show child attributes
Show child attributes
False when we do not serve this law's text; reason says why. A page with available: false is not charged.
true
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.
held, withheld, not_held, pending "held"
Server-side time for this request in milliseconds, excluding network transit. Not billed on.
747.5
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).
2
Date the governor approved the act, ISO 8601, as on GET /us/session-laws/{sessionLawId}: never an effective date. Null when none is held. See approvedDateDerived.
"2025-06-14"
True only when approvedDate was filled from the publisher's dated signature label rather than held as the approval date. See the same field on GET /us/session-laws/{sessionLawId}.
false
The official page or file this text was collected from, on a government publisher's host. Present on EVERY outcome, a withheld or not-held law included, so a reader is never at a dead end: where we do not serve the text, this is where to read it. Null only when we hold no government text source for the law.
"https://www.revisor.mn.gov/laws/2025/1/Session+Law/Chapter/1/"
Why available is false: not_held, withheld, pending or empty_text. Null when the text is served.
"withheld"
Present only when textStatus is withheld: what is wrong with the text we hold. A plain sentence to show, not an enum to branch on.
"LA: struck (deleted) law is merged into the text as if current while the record declares the markup preserved or absent (audit 2026-10-03). Awaiting a re-run with strike and underline recovery."
This page of the act's text, exactly as printed, in reading order. Concatenating every page's text in order reproduces the whole text. Where amendatoryMarkup is preserved, struck and inserted language is in the text in the convention markupConvention names. Null when available is false.
"CHAPTER 1--S.F.No. 1552\n\nAn act\n\nrelating to agriculture; ..."
Character offset in the full text where this page starts (0-based). Pages are contiguous: the next page's pageStart equals this page's pageEnd.
0
Character offset just past this page. A page is about 30,000 characters, cut back to a paragraph break where one is near, so the length varies.
29987
Length of the whole text in characters. This page is the last when pageEnd equals it.
310454
Estimated pages in the whole text: totalChars divided by the page size, rounded up. Pages are cut back to a paragraph break, so a long act can run a few percent over. Null when no text is served.
11
Credits charged for the page in this response, the same as creditsConsumed on this route: the price of one page when a page was served, 0 when none was (available is false). Each further page costs the same.
6
Estimated credits to read the rest of the text from pageEnd: the pages still to come (totalChars less pageEnd, over the page size, rounded up) times the price of a page. 0 on the last page. Null when no text is served. An estimate: a page cut back to a paragraph break can add a page on a long act. Reading a whole law costs estimatedBodyPages times the price of one body page (6 credits per page on GET /us/session-laws/{sessionLawId}/body). Ohio's 2025 budget act (HB 96) is 9,571,132 characters, about 320 pages or 1,920 credits to read in full; most acts are a page or two.
60
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.
"02693d5b55d4709bdd36fdccee4a9737d2655a3988a90daeb6b7c486ce50260b"
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.
"preserved"
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.
"publisher_literal"
Opaque cursor for the next page: send it as cursor on the same path. Null on the last page. It is tied to this exact text, so a cursor from before the text was re-collected is a 409.
"eyJ2IjoxLCJvIjoyOTk4NywiaCI6IjAyNjkzZDViNTVkNDcwOWIifQ"
True when more pages follow; fetch them with nextCursor. Each page is charged.
true
Was this page helpful?

