Skip to main content
What this gets you: turn a messy spreadsheet or database of topics and half-formed citations into a clean table of verified law, each row backed by a real citation and an official source link, without doing it by hand. Most rows in a spreadsheet like this already carry a citation, however roughly it is typed. Those do not need searching, they need resolving, and POST /us/statutes/resolve does 50 of them per call. The rows that carry only a topic are the ones that need POST /us/statutes/search. Split your inputs that way and the job is a handful of calls instead of one per row. Endpoints used: POST /us/statutes/resolve (2 credits per citation, up to 50 per call), POST /us/statutes/search (4 credits per call) for topic-only rows, and optionally GET /us/statutes/section/{actId}/body (6 credits) for full text.
Vaquill API Pro allows 30 requests per minute and 1,000 per day per key. Batching is what keeps you inside both: 500 citations is 10 resolve calls, not 500 search calls. Honor the Retry-After header on a 429. See Rate limits.

Start with the rows that carry a citation

Chunk the citation column into batches of 50 and resolve them. Every input gets an entry back, in order, so an unresolved row is flagged rather than silently dropped.
Python
Scope the whole batch with state or corpusType when every row belongs to one jurisdiction or corpus. A spreadsheet that spans jurisdictions should omit both and let each citation name its own.
Duplicates are collapsed before pricing, so a column with the same citation on forty rows costs one resolution. Read creditsConsumed on the response rather than multiplying 2 by your row count.
A resolved row has no excerpt: an excerpt is windowed around a query match, and resolving a citation runs no query. Take the excerpt from the search path, or fetch the body when a resolved row needs quotable text.

Then search the rows that are only a topic

A row with no citation to resolve needs a search. Scope with corpusType and state where you know them, and use fields to return only what you are writing back onto the record.
Python
Reach for matchType: "phrase" when the topic column holds a defined term or a statutory phrase rather than a question. It forces an exact-phrase match instead of hybrid semantic ranking, which is what you want when the wording is the identifier.
Every match carries several optional source URL fields, and any of them may be null. Use the canonical preference order in Text Formats and Source URLs rather than writing your own, so a citation gets the same official link everywhere in your product.
Python
Example output:

Optionally attach full text

When you need the whole section rather than the excerpt, take the actId from a match and call the body endpoint. It returns text only when it is available, and charges nothing when it is not.
At 6 credits a section this is the expensive half of the job, so fetch bodies only for the rows that need quotable text. If you are enriching a topic row and know up front you want the text, "includeBody": true on the search returns it inline for the same 4 + 6 per row, saving the round trip rather than the money.
Store the returned actId on your record. It is the stable handle for that section, so a later job can re-fetch metadata or full text without searching or resolving again.

Resolve a citation

The resolve routes in full, including what an unresolved verdict means.

Compare a rule across states

Run one query across several states and build a comparison.

Add the corpus as an agent tool

Expose statute search to a tool-calling agent.

Monitor corpus coverage

Detect when new corpora appear so you can re-enrich.
Last modified on September 6, 2026