curl --request POST \
--url https://api.vaquill.ai/api/v1/us/session-laws/batch \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"ids": [
"SSL_MN_2025R_G_Y2025_C1",
"S.F.No. 1552",
"SSL_MN_1801R_G_Y1801_C9999"
]
}
'import requests
url = "https://api.vaquill.ai/api/v1/us/session-laws/batch"
payload = { "ids": ["SSL_MN_2025R_G_Y2025_C1", "S.F.No. 1552", "SSL_MN_1801R_G_Y1801_C9999"] }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({ids: ['SSL_MN_2025R_G_Y2025_C1', 'S.F.No. 1552', 'SSL_MN_1801R_G_Y1801_C9999']})
};
fetch('https://api.vaquill.ai/api/v1/us/session-laws/batch', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.vaquill.ai/api/v1/us/session-laws/batch"
payload := strings.NewReader("{\n \"ids\": [\n \"SSL_MN_2025R_G_Y2025_C1\",\n \"S.F.No. 1552\",\n \"SSL_MN_1801R_G_Y1801_C9999\"\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}Get many state session laws at once, by id or citation
Read up to 50 STATE session laws in one call: the same record
GET /us/session-laws/{sessionLawId} returns, for each id or citation in ids, in the order
you sent them. Use it when you are enriching a list (a sync job holding ids from
/us/session-laws/list or /us/session-laws/changes, or an agent handed a memo with a dozen
session-law citations) instead of making a round trip per law.
When to use this instead of POST /us/statutes/resolve. resolve identifies a citation: it
costs 2 credits per item and answers with a sessionLaw match block (title, dates, source
link), never the record or its text. This route returns the FULL record of each law, the same as
GET /us/session-laws/{sessionLawId} (2 credits per law served), and optionally the
first page of its text. Use resolve to find the id; use this route to read many laws in one
request instead of one call each, which matters under the default limit of 30 requests per
minute per key (a plan raises it).
Cost: 2 credits per law served, the single-lookup price. Batching buys a round
trip, not a discount. A miss is not charged, and creditsConsumed reports what was actually
billed. A citation in ids also pays the /us/statutes/resolve price (2 credits), as its
own ledger line, exactly as GET /us/session-laws/{sessionLawId} charges a citation: kept
whenever we gave a verdict (a served law, an unresolved or ambiguous citation) and refunded
when the session it points into is only partly collected, or when we failed. An id never pays it.
Every entry gets an answer. A served law is in laws; every other entry is in notFound with
a status (not_found, unparsed, ambiguous, needs_jurisdiction,
session_not_fully_collected, federal, or error), the finer reason the single route’s 404
carries, the same detail sentence, and the candidates where there are any. Nothing is dropped
silently, so a shorter laws array can always be explained: every distinct entry is in laws,
in notFound, or, when it names a law an earlier entry already produced, in resolved.
Mapping your inputs. laws holds only the laws served, so it cannot be zipped back onto
ids by position. Each law carries the input it answers, and resolved lists every input that
did not match an id exactly as sent (a citation, or an id cleaned of whitespace, quotes or a
trailing period) with the id it resolved to. Duplicates are collapsed first: an entry
identical to an earlier one after trimming is dropped, order is kept, and deduped says how many.
Two DIFFERENT spellings of one law (S.F.No. 1552 and its sessionLawId) serve the law once,
under the first, bill it once, and list both in resolved.
Statuses. resolved: served. not_held: served as a record, because the registry knows the
law and holds no text (textStatus: not_held). not_found: no law carries the id. unparsed: it
reads as a citation no state session law we hold carries. ambiguous: the citation names several
laws, all in didYouMean, none chosen for you (Minnesota 2025 has two Chapter 1s).
needs_jurisdiction: a form several states share, naming none: send jurisdiction or name the
state. session_not_fully_collected: nothing matched in a session we have only partly collected, so
the law may exist; never charged, the resolve price included. federal: federal acts are served
by /us/statutes/section. error: WE failed to look this entry up. It says nothing about the law, is refunded
in full, and is safe to resend.
jurisdiction scopes how every citation in the batch is read, exactly as the same parameter
does on the single route. It is validated before anything is charged: a bad code is a 422.
includeBody adds the FIRST page of each served law’s text (about 30,000 characters)
as body, at the body price (6 credits) per law whose page is served, charged after the
work because the count is not knowable up front. It multiplies with the batch: 50 laws at
2 + 6 is 400 credits in one call. A law whose text is withheld, not held or
empty returns body: null and is not charged for it. If your balance cannot cover the pages after
the records were served, the records are returned without bodies rather than taking the paid-for
result away. bodyNextCursor continues a longer act on GET /us/session-laws/{sessionLawId}/body.
Limits. At most 50 DISTINCT entries after duplicates collapse (a raw list of up to
200 is accepted so a run of repeats is not refused), each at most 300 characters. An
empty list, an over-long entry, more than 50 distinct entries or a bad jurisdiction is a 422
and nothing is charged. At most 8 lookups run at once inside a call.
Example
curl -X POST "https://api.vaquill.ai/api/v1/us/session-laws/batch" \
-H "Authorization: Bearer $VAQUILL_API_KEY" -H "Content-Type: application/json" \
-d '{"ids": ["SSL_MN_2025R_G_Y2025_C1", "S.F.No. 1552"], "jurisdiction": "mn"}'
const res = await fetch("https://api.vaquill.ai/api/v1/us/session-laws/batch", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.VAQUILL_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ ids: ["SSL_MN_2025R_G_Y2025_C1", "S.F.No. 1552"], jurisdiction: "mn" }),
});
const { laws, notFound, resolved } = await res.json();
const byInput = new Map(laws.map((law) => [law.input, law]));
Related endpoints
GET /us/session-laws/{sessionLawId}is the single read, with the same record and rules.GET /us/session-laws/listandGET /us/session-laws/changesare where the ids come from.GET /us/statutes/resolvepoints a session-law citation here.
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.
Rate limits. Calls count against your key’s per-minute budget like every Data API route; a
429 carries Retry-After.
curl --request POST \
--url https://api.vaquill.ai/api/v1/us/session-laws/batch \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"ids": [
"SSL_MN_2025R_G_Y2025_C1",
"S.F.No. 1552",
"SSL_MN_1801R_G_Y1801_C9999"
]
}
'import requests
url = "https://api.vaquill.ai/api/v1/us/session-laws/batch"
payload = { "ids": ["SSL_MN_2025R_G_Y2025_C1", "S.F.No. 1552", "SSL_MN_1801R_G_Y1801_C9999"] }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({ids: ['SSL_MN_2025R_G_Y2025_C1', 'S.F.No. 1552', 'SSL_MN_1801R_G_Y1801_C9999']})
};
fetch('https://api.vaquill.ai/api/v1/us/session-laws/batch', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.vaquill.ai/api/v1/us/session-laws/batch"
payload := strings.NewReader("{\n \"ids\": [\n \"SSL_MN_2025R_G_Y2025_C1\",\n \"S.F.No. 1552\",\n \"SSL_MN_1801R_G_Y1801_C9999\"\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
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).
Body
Batch read. See POST /us/session-laws/batch.
State session law ids, or citations, to read: up to 50 DISTINCT entries per call. Each entry is read exactly as the {sessionLawId} path of GET /us/session-laws/{sessionLawId} reads one: an id as sent, an id carrying copy damage, or a citation (Minn. Laws 2025, ch. 1, P.A. 104-0001, S.F.No. 1552). Entries are trimmed, and blank ones are dropped. Duplicates collapse FIRST (the first spelling wins, order is kept) and the cap applies to what is left, so a list quoting one law sixty times is one entry; deduped says how many were collapsed. 200 is the ceiling on the raw list. An entry is at most 300 characters. Federal ids (SAL_...) and endpoint names are misses, never charged.
1 - 200 elements[ "SSL_MN_2025R_G_Y2025_C1", "S.F.No. 1552", "SSL_MN_1801R_G_Y1801_C9999" ]
Optional two-letter state code that scopes how a CITATION in ids is read, for the whole batch: it supplies the state a citation does not name (Laws 2025, ch. 1 with mn is Minnesota's) and vetoes a state the citation does name or whose form belongs to another. Ignored for an id. The 50 states, dc and pr, case-insensitive; federal or an unknown code is a 422 and nothing is charged. Omit it for a batch whose citations each name their own state.
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 "mn"
Return the FIRST page of each served law's text inline, on body, beside the record. A page is about 30,000 characters and costs the ordinary body price (6 credits) per law whose page is served; a law whose text is withheld, not held or empty comes back with body: null and is not charged for it. Read creditsConsumed rather than computing the total: the worst case is 50 laws at 2 + 6 credits each. bodyNextCursor continues a longer act on GET /us/session-laws/{sessionLawId}/body.
true
Response
One entry per distinct id or citation: the served laws in laws, every other entry in notFound with its status and reason. Not every entry is a law: read notFound, and creditsConsumed for what was billed.
Response for POST /us/session-laws/batch.
The served laws, one per distinct law, in the order the entries of ids first reached them. Entries that missed are NOT here, so a client mapping by position must use input on each law (or notFound), never the index.
Show child attributes
Show child attributes
One entry per input that did NOT match an id exactly as sent: a citation resolved to its law, or an id cleaned of whitespace, quotes or a trailing period. Includes an input whose law an earlier entry already produced, so every input is mappable onto laws. Empty when every entry was an exact id.
Show child attributes
Show child attributes
Every entry that did not yield a law, in the order sent, each with its status and reason. A miss is not charged for the route's own price. An entry read as a CITATION also paid the resolve price, kept whenever we gave a verdict on it (unparsed, ambiguous, needs_jurisdiction, federal) and refunded on session_not_fully_collected and error. An id pays no resolve price.
Show child attributes
Show child attributes
Number of laws returned in laws.
2
How many entries of ids were collapsed because an earlier entry was identical after trimming. Zero when none were. Two DIFFERENT spellings of one law are not counted here: the law appears once in laws and both spellings are in resolved.
0
Credits actually charged for this call, never the list price: 2 per law served, plus the 2-credit resolve price for each citation whose resolution is kept, plus the body price per body served. Misses and failures bill nothing for the route.
4
Server-side time for this request in milliseconds, excluding network transit. Not billed on.
512.3
Was this page helpful?

