The build-versus-buy conversation for internal legal search usually stalls in the same place. Buying a closed product means bending your workflow to someone else's UI. Building from scratch means a team of engineers spending a year collecting decisions, cleaning them, chunking them, embedding them, and standing up a retrieval stack — before a single lawyer runs a single query. Both roads are long.
There's a third road. BatasDB exposes its search as an API, so the corpus and the retrieval are a call away, and the thing you build is only the part that's specific to your firm: the interface, the access rules, the way results slot into a matter. This is a practical walkthrough of how a developer wires that up.
What you actually get from one endpoint
The whole product surface is a single endpoint: POST /search. Behind it is
the same pipeline that powers the BatasDB app — natural-language and exact-citation
retrieval running together, fused, weighted by legal authority, reranked, and (when the
query is a question) synthesized into a cited answer. It searches roughly
508,000 authoritative Philippine legal documents and returns in well
under a second.
You don't scrape the e-Library. You don't maintain a vector database. You don't own the problem of noticing that a provision was amended last quarter. What's in scope:
Quickstart: your first call
API access is part of the Solo Pro plan. Once you're on it, open the dashboard, go to API Keys → Create key, and copy the value it shows you. It's displayed once at creation and never again, so store it the way you'd store any secret — as a server-side environment variable, never in client code or a committed file.
Every request carries that key in the X-API-Key header. Here's the whole thing:
curl -s -X POST https://api.batasdb.ph/v1/search \
-H "X-API-Key: $BATASDB_KEY" \
-H "Content-Type: application/json" \
-d '{"query":"employer liability for illegal dismissal","limit":5}' That's a complete integration in five lines. Send a natural-language query, get back ranked, citable results. No query language to learn, no keyword gymnastics.
Reading a result
The response is plain JSON. Here's a single hit, trimmed to the fields you'll actually render:
{
"total": 5,
"latency_ms": 187.4,
"results": [
{
"id": "9590",
"score": 0.2814,
"doc_type": "sc_decision",
"case": {
"gr_number": "G.R. No. 214546",
"title": "PLDT v. Citi Appliance M.C. Corporation",
"date": "2019-10-09",
"division": "THIRD DIVISION",
"is_overruled": false
},
"statute": null,
"match": {
"excerpt": "…the relocation plan submitted by PLDT…",
"heading": "PLDT v. Citi Appliance M.C. Corporation"
},
"source": {
"url": "https://elibrary.judiciary.gov.ph/…/66296",
"doc_id": 66296
}
}
]
} A few things worth knowing before you build the UI:
Results arrive pre-sorted. The score is a cosine
distance, so lower is closer — 0.28 is a tighter match than
0.55. You don't need to re-sort; just render top to bottom.
Each hit is one of two shapes. When doc_type is
sc_decision, the case block is populated (G.R. number, title,
date, division, and an is_overruled flag) and statute is null.
When it's statute_provision, the reverse. Branch on doc_type
and you've handled both.
Every result deep-links to the source. source.url points
at the official document — the SC e-Library, the Official Gazette, and so on. Wire your
"open source" button straight to it, and your users land on the authority itself, not a
paraphrase of it. That single habit is what keeps an internal tool trustworthy.
The three levers you control
The request body has exactly three knobs, and they cover almost everything an internal tool needs:
query — the search string, up to 2,000 characters. Pass
the user's words as they typed them. "Getting scammed by a car dealer" and
"G.R. No. 214830" both work; you don't translate one into the other.
limit — how many results, 1 to 50 (default 10). Ten is
plenty for a results list; raise it when you're feeding a downstream step.
doc_type — narrow the corpus to sc_decision
or statute_provision, or omit it to search everything. A "case law only"
toggle in your UI is one field:
{"query":"warranty against hidden defects","doc_type":"statute_provision","limit":8}
When a query reads like a question rather than a lookup, the API can return a synthesized
answer — prose with inline [id] markers that map back to the
results you were already given, plus a disclaimer that it's legal
information, not advice. And when the retrieved sources don't actually answer the
question, it says exactly that instead of inventing a ruling to fill the gap. For an
internal tool, that refusal is a feature: a known "not found" is safe; a confident
fabrication is the thing that gets cited into a pleading.
Wiring it into your app
The architecture is deliberately boring, which is what you want. Your internal UI talks
to your backend; your backend holds the X-API-Key and calls
POST /search; the results come back and you render them. The key never
touches the browser. That's the entire shape.
Two things to handle for production. First, respect rate limits: if you get a
429, the response includes a Retry-After header telling you how
many seconds to back off — honor it rather than hammering. Second, cache. The same
handful of questions get asked across a firm all week; a thin cache on
query keeps things fast and quiet. The other status codes are the ones
you'd expect — 401 for a missing or revoked key, 422 for a
malformed body.
The division of labor
You own the product — the interface, the permissions, the way a result becomes part of a matter. We own the corpus — collecting it, keeping it current, and knowing which authority still governs. Neither side has to do the other's job.
Why not just build the corpus yourself?
Because the corpus is the part that never ends. Collecting every Supreme Court decision is only the first day. After that you're maintaining it: catching the amendment that quietly superseded a provision, so your tool stops surfacing a repealed rule as if it were live. Weighting a two-line Civil Code article above a long, keyword-dense decision that merely mentions the topic. Splitting documents along the law's own structure — section, article, paragraph — so a result is a whole, quotable unit instead of half a rule stitched to an unrelated paragraph.
None of that is glamorous, and all of it is the actual work. We wrote about why in the hard part isn't the AI, it's the law, and about verifying that a case is still good law in what a case citator is and why Philippine research needs one. The API is how you skip straight to the finished version of that work and put it behind your own search box.
The short version: a grounded Philippine legal
corpus, hybrid retrieval, and authority-aware ranking — reachable with one
authenticated POST. Everything above the search box is yours to design.