Legal Research API Integration: Pull Statutes Into Your App

The internal tool nobody wants to build twice

Every company that touches contracts eventually builds a small internal app that needs a slice of legal research. Sales wants to know if a clause holds up in Texas. Compliance wants RFPs flagged against the right federal statutes before they get routed. Legal wants intake tickets auto-tagged so the queue does not turn into a slush pile by Thursday afternoon.

The classic answer is to wire it all to a human. Legal answers Slack pings, paralegals tag tickets, ops reads RFPs at 9pm. That works until volume outgrows it.

The second answer, more recent, is to plug an AI legal research tool with API access into the internal app and let the tool answer the routine questions inline.

That is what this post is about. Not "what is an API," not a feature sheet, but a working pattern for pulling statute-grounded legal answers into the apps your team already uses, with one example stack (the Vaquill AI US primary law API) and a clear map of which questions an internal app should hand to a person.

Reviewed and corrected on 12 October 2026. This is general information, not legal advice.

Short answer: legal research API integration to pull statutes into your app is three calls. Authenticate with a bearer token, POST /us/statutes/search to find the on-point section, then GET /us/statutes/section/{actId}/body to render the full text with its citation. Cache the repeats server-side, log every citation, and send questions about how courts read a statute to a person. The full walkthrough, with real curl and code, is below.

TL;DR

  • A primary-law API exposes US primary law (US Code, CFR, Federal Rules, state codes for 56 jurisdictions, state constitutions, state court rules, and Executive Orders and Proclamations from 2015 on), which is enough to answer a surprising share of internal-app questions.
  • Auth in the example stack is a single Authorization: Bearer ... header, JSON request and response. Credit costs per endpoint are published at GET /api/v1/api-credits/pricing, which needs no key.
  • The pattern that works inside internal apps: keep the API key server-side, cache repeat questions, log the citation each answer cites, and give questions about how courts have read a statute to a named person.
  • The API is scoped to primary law text: statutes, regulations, rules and constitutions, found by search or by citation. Court opinions and docket data are a different kind of source, covered below.
What would you do?
Question 1 of 4

Your internal contract panel caches answers by the clause text alone. Yesterday a Texas deal asked about a clause. Today a New York deal asks about the same clause. What happens?

Part of our MCP and developer guide series.

For related MCP / API / developer coverage, see Legal API in 2026: What It Is, What It Returns, and How to Pick One, how to pull US Code sections programmatically, legal API vs building a RAG pipeline on cost, and how to embed a legal AI chat widget in your client portal.

Three internal apps that benefit immediately

Before the code, the use cases, because the wrong starting point is "we should add legal AI somewhere." Pick a workflow that is currently bottlenecked on a human reading a statute.

Sales: "is this customer in a state where our clause Y is enforceable?" Deals can stall while a deal desk waits days for a redline. If your contract has a non-compete, a class-action waiver, or an arbitration clause, enforceability varies by state in ways your AE will never memorize.

A small panel inside Salesforce that takes "clause name + customer state" and returns the relevant state statute section and a plain-English summary of the rule replaces a days-long wait with a lookup that takes seconds. Legal still owns the final call. Sales stops blocking on it for every routine deal.

Compliance: incoming RFP pre-screening. If you respond to government or regulated-industry RFPs, the first read is mostly mechanical. Which federal statutes does this touch? Is there a HIPAA flag, a GLBA flag, an OFAC flag, a state data-residency requirement?

You can pipe each incoming RFP through an internal app that runs the relevant text against the statutes search endpoint, pulling the federal statutes implicated by the data-handling section of the RFP. It is not the final compliance review. It is the trim that gets the document into the right reviewer's inbox half a day faster.

Legal intake auto-tagging. Every in-house legal team has an intake form. The form fills up. Tickets sit until someone manually categorizes them.

An internal app that classifies each ticket against a fixed taxonomy ("employment matter, IP matter, commercial dispute, regulatory") by asking the API to identify the applicable statute family is unglamorous and reliably valuable. It also gives you clean tags to report on six months later when someone asks "what is legal spending all our cycles on."

Notice what these have in common. They are internal, the audience is your own team, and a wrong answer gets caught by a downstream human review before it hits a customer or a court.

That is the sweet spot for legal-research APIs inside line-of-business apps. The same pattern is exactly wrong for, say, a public-facing chatbot that answers "should I sue my employer."

A quick story: the ban that wasn't

Take the sales example above, "is this non-compete enforceable?", and look at what happened in 2024. On April 23, 2024 the Federal Trade Commission announced a final rule banning noncompetes nationwide. Its press release said that under the rule, "existing noncompetes for the vast majority of workers will no longer be enforceable after the rule's effective date." A summary of that announcement would have looked authoritative for about four months.

On August 20, 2024, a federal district court issued an order stopping the FTC from enforcing the rule. The FTC appealed on October 18, 2024, and on September 5, 2025 it took steps to dismiss its appeal in the Fifth Circuit. Today the FTC's own announcement page carries a notice at the top: "The Noncompete Rule is not in effect and it is not enforceable." (FTC, Announces Rule Banning Noncompetes.)

So the practical answer for your sales team went back to the state-by-state patchwork. As one Hacker News commenter put it in 2024, discussing the era before the rule: "The enforceability varied widely from state to state." (tzs, Hacker News, June 2024.) Two lessons for an internal tool. First, a press release can be accurate on the day it is issued and out of date soon after, even when it comes from the agency itself, so link to the controlling text and show when it was last checked. Second, any panel that answers "is X enforceable" should treat its output as a starting point for the person who owns the decision.

What you can pull from the statutes API

Here is the actual surface, scoped precisely so you know what you are signing up for.

  • US Code and the Code of Federal Regulations, with section-level access.
  • Federal Rules of Procedure: FRCP (civil), FRCrP (criminal), FRE (evidence), FRAP (appellate), and FRBP (bankruptcy).
  • State statute codes for 56 jurisdictions (50 states, DC, Puerto Rico, Guam, the Northern Mariana Islands, the US Virgin Islands and American Samoa), over 2 million sections, each indexed by the state's official code structure. Call GET /api/v1/us/statutes/coverage for live coverage and GET /api/v1/us/statutes/divisions?corpusType=STATE&state=tx for one state's codes.
  • The U.S. Constitution and state constitutions, plus state court rules.
  • Executive Orders, Presidential Proclamations, and Presidential Memos issued since 2015, from the Federal Register.
  • A statutes search endpoint (POST /api/v1/us/statutes/search) that runs hybrid semantic and keyword retrieval over the corpora above and returns ranked, section-level hits with citations. The whole collection is over 5.3 million sections.

Two things sit outside that list, and a good internal app plans for them from day one.

Court opinions. The API returns the text of the law as written. How a court has read a phrase like "legitimate business interest" lives in opinions, which are a separate kind of source. See our guide to the CourtListener API if you need to wire one in.

Docket and filing data. Complaints, motions and docket entries come from court systems, not from statute text. Our PACER API guide covers what the federal courts offer and what they charge.

The list is deliberately specific because the alternative, vague "comprehensive legal data" claims, is how internal apps end up with half-broken integrations after the demo.

A statutes-first default works for most internal apps because the questions sales, compliance, and intake actually ask are statutes-shaped: "is X enforceable in state Y," "which federal act covers Z," "which subsection controls this fact pattern." Those collapse to a section pull plus a short summary.

The design needs a second path the moment the question is interpretive: "how have the courts construed 'legitimate business interest' in the Eleventh Circuit." That is a question about opinions, and forcing it through a statute lookup produces confident, wrong answers.

The request shape

The whole API is JSON over HTTPS with bearer auth. The first call you make in any new integration looks exactly like this.

curl -X POST https://api.vaquill.ai/api/v1/us/statutes/search \
  -H "Authorization: Bearer vq_key_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{ "query": "non-compete agreement", "corpusType": "STATE", "state": "fl", "limit": 3 }'

You get back structured JSON with a results array and a creditsConsumed figure for the call. No SOAP, no XML-RPC, no GraphQL warmup, no OAuth dance. A bearer token over HTTPS, JSON in, JSON out. Note the state code is lowercase two letters (fl, tx, ny).

Each result carries an actId (a structured section identifier like USC_T42_C21_S1983), a citation, a breadcrumb of the title hierarchy, an excerpt and a relevanceScore. You never hand-build the actId. You take it from the search response and pass it to the section endpoint to fetch the full text:

curl https://api.vaquill.ai/api/v1/us/statutes/section/USC_T42_C21_S1983/body \
  -H "Authorization: Bearer vq_key_your_key_here"

That body call returns the full provision; the metadata-only variant at /us/statutes/section/{actId} returns the section's identity and hierarchy without the body text. The keys are prefixed vq_key_ so a leaked one is easy to grep for in logs. The API reference says to rotate any key that leaks.

For the broader anatomy of "what makes a good legal API response shape," the legal API guide walks through it. This post is going to assume you are ready to write code.

A working TypeScript wrapper

Here is the entire integration layer for an internal app. One file, two functions. Drop it behind your existing service routes.

// lib/legalApi.ts
const BASE = "https://api.vaquill.ai/api/v1";
const KEY = process.env.VAQUILL_API_KEY;
if (!KEY) throw new Error("VAQUILL_API_KEY missing");

const headers = {
  Authorization: `Bearer ${KEY}`,
  "Content-Type": "application/json",
} as const;

type CorpusType =
  | "USC"
  | "CFR"
  | "STATE"
  | "CONSTITUTION"
  | "STATE_CONSTITUTION"
  | "FEDERAL_RULES"
  | "STATE_RULES"
  | "EXECUTIVE_ACTION";

export async function searchStatutes(opts: {
  query: string;
  corpusType?: CorpusType;
  state?: string; // lowercase 2-letter, e.g. "fl"
  limit?: number;
}) {
  const res = await fetch(`${BASE}/us/statutes/search`, {
    method: "POST",
    headers,
    body: JSON.stringify(opts),
  });
  if (!res.ok) throw new Error(`statutes/search ${res.status}`);
  return res.json();
}

// actId comes from a search result (e.g. "USC_T42_C21_S1983").
// Never hand-build it. /body returns full section text.
export async function getSectionBody(actId: string) {
  const res = await fetch(
    `${BASE}/us/statutes/section/${encodeURIComponent(actId)}/body`,
    { headers },
  );
  if (!res.ok) throw new Error(`statutes/section/body ${res.status}`);
  return res.json();
}

And the calling code, the part that actually lives inside your internal app, looks like this. Imagine an endpoint behind your Salesforce or HubSpot UI that takes a clause name and a state and surfaces the controlling statute.

// app/api/clause-check/route.ts (Next.js handler, but the shape is universal)
import { searchStatutes, getSectionBody } from "@/lib/legalApi";

export async function POST(req: Request) {
  const { clause, state } = await req.json();

  // 1. Pull the underlying state statutes, if there are any on point.
  const statutes = await searchStatutes({
    query: clause,
    corpusType: "STATE",
    state: state?.toLowerCase(),
    limit: 3,
  });

  // 2. Take the top hit's actId and fetch its full text for in-panel rendering.
  const top = statutes.results?.[0];
  const section = top ? await getSectionBody(top.actId) : null;

  return Response.json({
    statutes: statutes.results ?? [],
    citation: top?.citation,
    section,
  });
}

Two calls. Less than fifty lines of code, including the wrapper. The search returns ranked hits with citations, the body call returns the full section text. Your internal UI renders them next to each other. The sales rep sees the citation, opens the section if they want, and reads the rule in context.

The thing to internalize: every field your user sees on screen has a verifiable source two clicks away. That is the property that makes an internal legal-research panel safe to ship.

Without it you are just generating confident text, which is exactly the problem that lands lawyers in front of judges for fabricated citations (more on that in how to verify AI legal citations before filing).

Patterns that actually hold up in production

A handful of patterns separate an internal legal panel that ships and stays useful from one that gets quietly turned off six weeks later.

Server-side proxy, always

Never put vq_key_... in the browser. Not in a NEXT_PUBLIC_ env var, not in a service worker, not in a "for now" dev key. Any internal app that calls the API should go: browser to your server, server to the legal API. The key lives only in your backend secrets store.

The Next.js documentation spells out why the NEXT_PUBLIC_ route is a trap: variables exposed to the browser "will be inlined into the JavaScript bundle during next build." (Next.js, Environment Variables.) Anything in that bundle is readable by every visitor. This sounds obvious until you watch a hackathon project ship a debug build with the key in the bundle. Treat the key the way you treat your Stripe secret. Same level of paranoia, and the same standard you should hold the vendor to when you work through the security and deployment questions to ask a legal data vendor.

Cache the obvious repeats

Sales asks "is a class-action waiver enforceable in California" three times a week. Compliance pings the same FCRA section every other RFP. Internal apps in particular have heavy query repetition because the questions come from the same business workflows.

A simple cache on (query, state) with a 24-hour TTL cuts your API spend whenever queries repeat, and improves latency for the user from sub-second to instant.

The most common cache failure is a key that omits the state. The Salesforce panel works fine in a Texas demo, then a New York deal asks the same clause question and reads the cached Texas answer.

Include every variable that changes the response in the cache key, normalize state codes to the lowercase two-letter form the API takes, and lowercase the query before hashing. Redis is overkill for most internal apps; an in-process LRU with a few thousand entries does the job. Skip the cache when the user has explicitly hit a "re-run" button, so they can refresh stale answers when they need to. If you would rather expire the cache on the law than on the clock, a webhook that fires when a statute or rule actually moves is the cleaner trigger.

Log the citation, not just the answer

Every time the API returns, log the query, the results, and the list of cited sections. Two reasons.

First, when your downstream reviewer (lawyer, compliance officer, whoever) flags a wrong answer, you need to see what the search returned, not just what your UI rendered. Second, six months in you will want to know which sections your team is hitting most often, because that tells you where to invest the next round of internal tooling.

A row per call in a Postgres table is enough. You do not need an observability platform for this.

Give interpretive questions a human owner

A real example: a customer in Florida asks whether their non-compete is enforceable. Florida's restrictive covenant statute is Fla. Stat. § 542.335, and a search for "non-compete agreement" scoped to Florida returned it third of three hits (after § 542.45, "Covered noncompete agreements," and § 542.33) when we ran the call shown above on 12 October 2026, with matchQuality at moderate. That is a reason to read the hits before trusting the first one.

But the interpretation of the statute, the cases that define what "reasonable time" and "legitimate business interest" mean, lives in court opinions. The statutes API returns the section text for embedding directly in your internal tool. Interpretive, opinion-level questions go to a lawyer, or to an opinion source your team already trusts.

The reason to split it that way is that the freshness profiles are different. Statutes change slowly and centrally. Court opinions land every business day across dozens of jurisdictions. Two specialized sources, one synthesis layer, is the cleaner architecture.

Loading diagram...

What it costs

Each call spends API credits, and the per-endpoint costs are published at GET /api/v1/api-credits/pricing, which needs no key. Every response reports creditsConsumed, so read that figure rather than assuming a list price. The spec says failed and refunded work bills 0, and a section-text call is charged only when text is returned. Multiply the per-call cost by your expected volume after caching, and that is the number to take to your finance partner.

The other quiet failure mode worth flagging: how reviewers handle low-confidence answers. The panel returns a citation, the sales rep treats it as gospel, and there is no signal in the UI that says "this was a weak match."

Surface the match signal explicitly. The search response carries matchQuality (exact, strong, moderate, weak, or none) and a noGoodMatch flag. Map exact and strong to green, moderate and weak to yellow, and none, noGoodMatch: true, or an empty result set to red, and treat any value you do not recognize as moderate. Both fields come back null when the API reports rankingDegraded: true, so treat null as a reason to look twice. The API's own description says strong was right about 94% of the time on its evaluation set and weak about 35%, which is why yellow and red need a person. Do not threshold on relevanceScore, which only orders the results within one response. Yellow and red route to a named human inside the workflow tool.

A panel that returns the same confident UI for "found the controlling subsection" and "found something vaguely on-topic" is the panel that quietly trains people to stop checking.

Researchers who tested legal AI tools made the same point about retrieval in general. Retrieving documents that "only seem textually relevant" but are ultimately irrelevant or "distracting," they wrote, "negatively affects performance on general question-answering tasks," and in law the problem is likely to compound because the applicable rule differs by jurisdiction and time period. (Magesh et al., "Hallucination-Free?", 2024.) A weak-match warning is the cheap UI answer to that finding.

Where this approach falls short

Three places to flag the limits before you hand this to a junior engineer.

Active litigation strategy. If a partner needs to brief a novel issue with adversarial cases and treatment analysis, a statute lookup is the wrong tool. They need a research workflow built around opinions. The API is a developer tool for embedding sourced statutory answers into other software.

Anything safety-critical without human review. Internal app does not mean "no review." It means "the review happens inside the company."

Build the panel so that a thin or empty result set routes to a human, every time. If the API returns zero or one hits on a question your team thinks is well-trodden, treat it as low-confidence and surface it differently.

Jurisdictions outside the US. The API covers US primary law. If your internal app needs UK, EU, or Australian law, you need a different source for those layers.

FAQ

How do I integrate a legal research API into my internal app? Three steps. Store the API key in your backend secrets, never the browser. Call POST /us/statutes/search with the user's query and a corpus filter to get ranked sections with citations. Then call GET /us/statutes/section/{actId}/body with the top hit's actId to render the full text. Cache by (query, state) and log every citation.

Can I pull statutes into my app with an API? Yes. A statutes API returns US primary law (US Code, CFR, Federal Rules, state codes, constitutions, Executive Orders) as structured JSON with citations. You search by natural-language query, get back section-level hits, and fetch the full text of any section by its actId. The example in this post does it in under fifty lines of TypeScript.

Does this API return court opinions? It returns primary law text: statutes, regulations, court rules and constitutions. How courts have read a statute is a question about opinions, which come from a separate source. Plan a route to a lawyer or an opinion source for those questions.

How much does legal data API integration cost? Each call spends credits, and the per-endpoint costs are published at GET /api/v1/api-credits/pricing without a key. Each response reports creditsConsumed, and failed work bills 0. Caching repeat questions lowers what you spend.

How do I authenticate with the API? A single header: Authorization: Bearer vq_key_.... JSON in, JSON out, over HTTPS. No OAuth flow. Keep the vq_key_ token server-side and proxy every call through your own backend so it never reaches the browser.

What format are the statute results in? Each search result carries an actId (a structured identifier like USC_T42_C21_S1983), a citation, a breadcrumb of the title hierarchy, and an excerpt. You pass the actId, not a hand-built citation, to the section endpoint to fetch full text.

How do I keep cached legal answers from going stale? Include every variable that changes the answer in the cache key (query plus state, normalized), set a 24-hour TTL, and add a "re-run" button that skips the cache. Statutes change slowly, so a day-long TTL is a reasonable start. If you want the cache to expire on a real change, use a law change watch instead of the clock.

Where to go from here

If your project this quarter is a Salesforce panel that surfaces controlling statutes in your customer's state, an intake bot that routes legal requests by statute family, or a compliance trim that flags RFPs against federal acts, this is a sound starting pattern: search plus section-body, a server-side wrapper, cached repeats, logged citations, and a human fallback on anything thin.

To try it on your own app, start from the US primary law API, generate a key, and run the curl above.

Connect our US primary law database.
Every US statute, regulation, constitution, and executive order via REST, MCP or SQL. 5.4M+ sections, section-level citations, and links to the official source. Plus a free open dataset.
Updated October 12, 202622 min read

New legal AI guides, weekly.

Priyansh Khodiyar

Priyansh Khodiyar

Co-Founder & CTO

Priyansh leads engineering and AI at Vaquill AI: the pipelines that pull statutes, regulations and court rules from every US jurisdiction's official publisher, and the REST API, MCP server and open dataset that serve them.