All articles

Build on BatasDB

Build your firm's internal law search on the BatasDB API.

Every firm re-researches the same questions, and the answers live scattered across inboxes, shared drives, and someone's memory. You could build an internal search tool — but the hard part was never the search box. It's the grounded, up-to-date, effectivity-aware corpus behind it. One POST /search call gives you that. You build the part your people actually see.

Jeffrey Valdehueza 8 min read

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:

Supreme Court decisions Court of Tax Appeals Sandiganbayan Republic Acts & the Constitution The Labor Code & codes Agency issuances — BIR, BSP, SEC, DOLE, DILG

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 closer0.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.

API access is included in Solo Pro.

Grab a key, run the curl above against a question you can already verify, and see the results land on the real authority. 7-day free trial, no credit card.