Skip to main content
    All articles

    Using a Semantic Search API for Indian Case Law

    23 June 202612 min readCourtMesh Team
    Cover card headed Semantic Search Fails Plausibly, with the line: the expensive failure

    Most developers integrate a semantic search endpoint the way they integrate any other search endpoint. They post a string, read the array that comes back, and move on. Then a lawyer complains that a query returned nothing useful, and there is no obvious way to debug it, because from outside the call it looks like a black box that took prose and returned judgments.

    It is not a black box. A semantic search call over Indian case law is a short pipeline with five distinct stages, and every confusing result you will ever see is attributable to one of them. Once you know what the stages are, the endpoint stops being mysterious and starts being a tool you can reason about: you can tell why a filter was applied that the user never asked for, why a three word query behaved worse than a three sentence one, and why a query consisting only of a case number quietly turned into a keyword search.

    This piece walks the pipeline as it actually runs on POST /search/cases/semantic at https://research.courtmesh.ai/api/v1/prod, then covers the practical consequences: what to send, what to expect back, and where the endpoint is the wrong instrument entirely. The mechanism is the documentation.

    What the Endpoint Takes

    The request body is close to the keyword endpoint's, with one important difference in the query field and one addition. The query is required and must be at least three characters, and unlike keyword search it is meant to be prose. Alongside it you may pass court, caseType, caseNumber, judgeName with its aliases judges and judge, year, fromDate and toDate in strict YYYY-MM-DD, page, and limit up to a maximum of 100. There is also an optional filters object.

    That filters object is worth understanding early, because it is the control surface. The pipeline extracts filters from your prose automatically. Anything you pass explicitly overrides what the model extracted. So if a user's phrasing keeps producing an unwanted court restriction, the fix is not to rewrite the prose, it is to pass the filter you want explicitly and let it win.

    The single most useful fact about this endpoint

    Explicit filters beat extracted filters. The model reads your prose and infers structure from it. Whatever you supply in the request body sits on top of that inference. When behaviour surprises you, the debugging move is to take control of the filters rather than to argue with the sentence.

    The Five Stages, in Order

    Here is what happens between your request and the array you get back. Each stage is doing something specific, and each has a characteristic failure.

    1

    Normalisation of the raw query

    Before anything else, the query is scanned for identifiers written the way Indian practitioners write them. A pattern like 123-2024, or 123/2024, or an alphanumeric prefix followed by a year, is collapsed into a single token and also captured separately as a candidate case number. This exists because a case number typed with a slash and a case number typed with a hyphen are the same number to a human and two different strings to an index. The stage is silent and lossless, and it is why pasting a case number into a semantic query is not immediately hopeless.

    2

    Filter extraction by a model

    The normalised query goes to a language model whose job is to pull structure out of prose and hand back a cleaned query with that structure removed. Ask for Delhi High Court judgments from 2019 on dishonour of cheque under section 138 and the extraction should return court and year as filters and leave the substantive legal question as the query. This is the stage that makes natural language search feel intelligent, and it is also the stage that occasionally applies a restriction the user did not intend, which is precisely what the explicit filters object exists to override.

    3

    Terminology expansion

    The cleaned query is then expanded and restructured into the vocabulary courts actually use, so that a description written in a client's words moves closer to the language of judgments before it is embedded. This is the quiet stage that does the most for recall, because the whole difficulty in Indian case law research is that the bench that decided your point in 1987 used words you would never type in 2026.

    4

    Embedding

    The expanded query is converted into a vector: a long list of numbers locating that text as a point in a space where passages with similar meaning sit near one another. This is the step that draws AI credit, and it is the reason the endpoint is billed differently from keyword search.

    5

    Vector retrieval and hydration

    The vector is used to retrieve nearest neighbours from the embedded corpus, with a minimum similarity score of 0.3 and offset based paging, filtered by the merged filter set. The matching case ids are then hydrated into full case records from the primary store, and each result carries a score alongside the usual case fields. That score is a distance, not a confidence, and it should be treated as a ranking aid rather than a percentage of anything.

    The fallback you need to know about

    If filter extraction strips so much out of the query that fewer than three characters of substantive text remain, there is nothing meaningful left to embed. Rather than returning noise, the request falls back to keyword search with the extracted filters applied, and the response's meta object carries fallbackMode set to opensearch. Read that field. A user who typed only a court name and a year got a perfectly good keyword result set, and your interface should not label it as an AI search.

    What Comes Back, and How to Read It

    The response follows the same envelope as every other endpoint: success, a data array, a meta object and pagination. The data entries are full case records, so you get title, court, caseType, judges, petitioners, respondents, decisionDate, disposalNature, acts, sections and the rest, plus the similarity score. Meta echoes the query, the applied filters and a responseTime, and carries the fallbackMode field when the fallback fired.

    Pagination on this endpoint behaves differently from keyword search and you should not treat the numbers as equivalent. Vector retrieval works by offset over a nearest neighbour list, so the total is an estimate rather than a count. What is reliable is hasMore, which is determined by fetching one result beyond the page and checking whether it exists. Build your interface on hasMore and a next control, not on a page count and a result total. Showing a user that there are exactly 1,342 semantically similar judgments implies a precision the mechanism does not have.

    Response fieldWhat it actually meansHow to use it
    scoreA similarity distance between the query vector and the retrieved passage, above a floor of 0.3Order results by it. Do not display it as a percentage match and do not set your own confidence threshold on top of it without testing against real queries.
    pagination.totalAn estimate derived from the current offset and whether another result existsDo not print it as a count. Use it only to decide whether a next control is enabled.
    pagination.hasMoreWhether at least one more result exists beyond this pageThe reliable paging signal on this endpoint. Drive your next button from it.
    meta.fallbackModePresent and set to opensearch when the query was filter only and keyword search ran insteadLabel the result set honestly in your interface, and consider prompting the user to add a description of what they are looking for.
    meta.appliedFiltersThe merged set of extracted and explicitly supplied filters that actually ranShow it. A user who can see that a court filter was inferred can correct it in one click instead of concluding the search is broken.

    How to Query Well, and Why Three Words Is a Waste

    The single biggest determinant of result quality on this endpoint is not any parameter. It is the length and specificity of the prose you send. Keyword search rewards precision in vocabulary. Semantic search rewards context, because meaning is what it operates on, and there is very little meaning in three words.

    The practical instruction to give your users, and to bake into your placeholder text, is to describe the matter the way they would explain it to a colleague. Not cheque bounce. Instead: the accused issued a cheque in discharge of a loan, it was returned for insufficient funds, the statutory notice was sent to an address the accused says he had vacated, and the question is whether service was validly effected for the purposes of proceedings under section 138 of the Negotiable Instruments Act 1881. That query gives the pipeline something to work with at every stage.

    • Send sentences, not keywords. Two or three full sentences describing parties, facts, relationship and the question in dispute will outperform a term of art almost every time on a conceptual query.
    • Let the prose carry the metadata, or pass it explicitly, but decide which. Writing Bombay High Court into the sentence and also passing court in the body is fine, and the explicit value wins. Doing neither and then wondering why results span every court is not.
    • Use it to harvest vocabulary, not only to find authority. Even where a semantic result is not the judgment you eventually cite, it shows you the phrasing benches actually use for your problem. Take those terms back to POST /search/cases and run an exhaustive keyword search with them. That two step move is where the real gain sits.
    • Do not send identifiers. A citation, a diary number or a case number carries almost no semantic signal. Every civil appeal number means roughly the same thing as every other one, so they all embed to nearly the same place. Send those to keyword search, where exact matching answers instantly and completely.
    • Do not rely on it for negation. Embedding models are weak at holding a not. A query about cases where consideration was absent sits very close to one about cases where consideration was present, because both are about consideration. In law that distinction is frequently the whole matter.
    • Do not use it for exhaustiveness. Nearest neighbour retrieval returns the closest results, not all matching results. If the task is every judgment of a given High Court under a given section since 2015, that is a keyword query with filters, and only keyword search can be complete over what has been indexed.

    Keyword search fails visibly and returns nothing. Semantic search fails plausibly and returns ten confident neighbours. The second failure is the expensive one, because it reads like a finished search.

    The Two Things Vendors Do Not Volunteer

    There are two facts about any semantic legal search product that decide whether it is useful to you, and neither of them appears on a marketing page. Ask about both, of every vendor, including this one.

    The embedded corpus is much smaller than the indexed corpus

    Keyword search on this API runs against roughly 310 million case records drawn from official government portals: the Supreme Court, all 25 High Courts, the district judiciary and tribunals including NCLT, NCLAT, ITAT, CESTAT, SAT, TDSAT and DRT. The semantically embedded corpus is a fraction of that, in the low millions. That is not a defect and it is not unique to any one vendor, because embedding a document is orders of magnitude more expensive than indexing its tokens. It does mean that a semantic query which finds nothing has not told you the authority does not exist, only that it is not in the embedded slice.

    The operational consequence is a search strategy rather than a single call. Use semantic search to discover the shape of the problem and the vocabulary of the courts, then use keyword search over the full corpus to close the set. Any product that presents semantic search as a replacement for keyword search is quietly narrowing the corpus its users are searching without telling them.

    It costs more, and it costs before it retrieves

    Three of the five stages involve a model. Filter extraction runs a model, terminology expansion runs a model, and embedding runs a model. All three happen before any retrieval, which means a semantic call has a cost floor even when it returns nothing at all. The endpoint also draws on your AI allowance and will return 403 when that allowance is exhausted, which is a state no amount of retrying will clear.

    Because it is doing this work, the endpoint is slow by the standards of a search box. It sets keep alive headers and allows up to ten minutes. Your client needs a matching timeout, a real pending state in the interface, and a call budget that respects the ten requests per minute allowed to an API key. Never put a semantic call in the path of a page load, and never let a user trigger one by typing.

    Presenting the score as a percentage match, which invites users to trust a distance as a confidence
    Printing pagination.total as an exact result count on an endpoint where it is an estimate
    Silently rendering a fallbackMode result set as an AI search
    Treating an empty semantic result as proof that no authority exists
    A client timeout shorter than the endpoint's own, so long searches fail on your side while succeeding on the server
    Retrying a 403 from the AI allowance as though it were a transient error

    Choosing Between the Two Search Endpoints

    Both endpoints are available under the same key and the same base URL, and the skill is routing the question to the right one before you spend a call.

    Send it to keyword search

    Citations, case numbers, CNRs, diary numbers, party names, judge names, anything where you already hold the exact string. Also anything requiring completeness, such as every judgment of a High Court citing a section since a given year, because exact matching with filters can be exhaustive over what is indexed and nearest neighbour retrieval cannot.

    Send it to semantic search

    A principle you can state but cannot name. A fact pattern as the client described it. A question in an area outside your usual practice where you do not yet have the vocabulary. Older authority on a doctrine that acquired its label later. These are the queries where the words you would type are not the words the judgment used.

    Use both, in this order

    Semantic first to discover the terms, keyword second to close the set. This is the single highest value pattern for a research product, and it is worth designing your interface around it explicitly rather than leaving users to discover it.

    Neither, and read instead

    Retrieval finds candidates. Whether a passage is ratio or obiter, whether the judgment survives later benches, and whether the distinction on the facts is real remains the advocate's work. No endpoint has read the judgment, and no score is a substitute for having read it.

    Build with that division in mind and the semantic endpoint becomes what it should be: a way of reaching judgments that share your facts rather than your words, over a corpus assembled from official sources, with an honest account of where its reach ends. The request and response reference lives at the API documentation, the corpus and coverage detail at the API overview, and what an AI call draws at API pricing.

    Search by meaning, then close the set by matching

    The CourtMesh API gives you both instruments under one key: POST /search/cases for exact, exhaustive, auditable keyword retrieval over roughly 310 million records from official government portals, and POST /search/cases/semantic for natural language description of a fact pattern over the embedded corpus, with filter extraction, terminology expansion and vector retrieval built in. Read the full parameter and response reference at the API documentation, check what an AI call draws at API pricing, and if you are wiring this into an assistant, see the MCP server page.

    Explore CourtMesh
    Semantic SearchAPIEmbeddingsAICase Law
    X LinkedIn