Supreme Court of India case data, as a REST API
One court, one canonical filter value, and a document trail that runs from the diary number a matter receives on filing to the judgment that closes it. This page documents how the apex court behaves inside the CourtMesh API: which parameters bite, which registry concepts have no parameter at all, and how to work around the second group honestly.
Two identifiers, one matter
Every apex court filing begins life with a diary number issued by the registry. It is a receipt: proof that papers were lodged, allotted before scrutiny, and carried through the whole defect curing period. Nothing about it tells you what kind of proceeding the matter is. Only after registration does the case acquire a case number, and only then does the case type become legible, whether the matter is a special leave petition, a writ petition, a transfer petition, a review or a contempt proceeding.
That two stage lifecycle is why apex court data is awkward to model. Practitioners quote the diary number while a matter is young and the case number afterwards, and the two never resolve into a single key. The CourtMesh corpus is built on the case number, because that is the identifier printed on the documents we hold. The diary number rides along as metadata where the registry published it, readable on the case record but not available as a filter.
The practical consequence is short. Identifiers you know go into the query string, where the parser recognises patterns such as 12345/2023 and normalises the punctuation away. Attributes of the decision, meaning who decided it, when, and in what document class, go into the typed filters.
| Registry concept | Where it lives in the API |
|---|---|
| Diary number | Free text in query. Returned as metadata.diaryNumber on the case record when captured. |
| Case number | Free text in query, and the key that /cases/:id/related groups documents on. |
| Coram and bench strength | No parameter. Filter judgeName instead, or count the judges array on each record. |
| Document class | caseType, which for this court takes Judgment, Order or Final Order. |
| Decision date | fromDate and toDate in YYYY-MM-DD form, or the year filter. |
Quickstart
The base URL is https://research.courtmesh.ai/api/v1/prod. Authenticate with your key in the X-API-Key header, or as Authorization: Bearer if that fits your client better. Keys are issued with a cm- prefix. The one filter that keeps this request inside the apex court is court.
curl -X POST "https://research.courtmesh.ai/api/v1/prod/search/cases" \
-H "X-API-Key: cm-YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{
"query": "special leave petition maintainability delay condonation",
"court": "Supreme Court of India",
"fromDate": "2020-01-01",
"toDate": "2024-12-31",
"limit": 20,
"sortBy": "relevance"
}'What comes back
Every route answers with the same envelope: a boolean success, the payload under data, request echo under meta, and page counters under pagination on list routes. Text fields are watermarked per account, so the prose you receive is traceable if it is redistributed.
{
"success": true,
"data": [
{
"id": "66f1c2a4e9b21d0012ab34cd",
"caseNumber": "...",
"title": "...",
"court": "Supreme Court of India",
"caseType": "Judgment",
"judges": ["..."],
"petitioners": ["..."],
"respondents": ["..."],
"decisionDate": "2023-09-21T00:00:00.000Z",
"disposalNature": "...",
"summary": "...",
"hasDocuments": true,
"hasAnalysis": true
}
],
"meta": {
"query": "special leave petition maintainability",
"filters": { "court": "Supreme Court of India" },
"responseTime": "812ms"
},
"pagination": {
"page": 1,
"limit": 20,
"total": 4310,
"totalPages": 216,
"hasMore": true
}
}Endpoints, read through an apex court lens
| Verb | Path | Behaviour at the Supreme Court |
|---|---|---|
| GET | /judges/search?q= | Substring match over 1,069 Supreme Court judge names merged with the High Court list. Returns up to 50 names. |
| POST | /search/cases | Keyword search over the full corpus. Set court to Supreme Court of India to stay inside the apex court. |
| POST | /search/cases/semantic | Vector search over the analysed subset. Court scoping goes inside the nested filters object. |
| GET | /cases/:id | Case record. Carries metadata.diaryNumber when the registry number was captured for that document. |
| GET | /cases/:id/analysis | AI analysis held separately from the record: headnote, issues, holding, doctrines, cited cases. |
| GET | /cases/:id/related | Every document sharing the same case number, up to 50, plus a date ordered timeline. |
| GET | /cases/:id/pdf | Encrypted download link for the stored judgment, valid for 3,600 seconds. |
| POST | /cases/:id/analyze | Queues analysis and answers 202 immediately. Poll the case record after 30 to 60 seconds. |
| POST | /cases/:id/analyze-consolidated | For an apex court matter this reads up to 20 documents sharing the case number and reasons over all of them. |
| POST | /request-timeline | Starts an order history job for a case and hands back a requestId. |
| GET | /get-timeline/:requestId | Polls that job and returns orders, orderCount and totalOrderCount when it finishes. |
| GET | /health | Liveness probe. The only route on the surface that takes no API key. |
Benches, coram and the judge index
A Division Bench of two decides most of the apex court list. Three judge benches take references and conflicts, and Article 145(3) reserves substantial questions on the interpretation of the Constitution for a bench of at least five, which is where the familiar seven, nine and eleven judge references come from. Bench strength is therefore the single most informative attribute of an apex court decision, and it is exactly the attribute no court portal publishes as a field.
Neither do we, and we would rather say so than invent one. What the API gives you instead is the judge index: 1,069 Supreme Court names covering sitting and retired judges, merged with the High Court list into 4,561 unique names that /judges/search matches on as a case insensitive substring, returning up to 50 per call. Resolve a name there first, because the spelling in the index is the spelling the filter expects. From that point, counting the judges array on the results is a reliable proxy for coram.
import requests
BASE = "https://research.courtmesh.ai/api/v1/prod"
HEADERS = {"X-API-Key": "cm-YOUR_KEY_HERE", "Content-Type": "application/json"}
# 1. Resolve the judge name the way the index spells it.
lookup = requests.get(
f"{BASE}/judges/search",
params={"q": "chandrachud"},
headers=HEADERS,
timeout=60,
)
names = lookup.json()["data"] # up to 50 matching names
print(names)
# 2. Pull that judge's Supreme Court matters, newest first by relevance.
body = {
"query": "constitutional validity fundamental rights",
"court": "Supreme Court of India",
"judgeName": names[0],
"page": 1,
"limit": 50,
}
res = requests.post(f"{BASE}/search/cases", json=body, headers=HEADERS, timeout=120)
payload = res.json()
print(payload["pagination"]["total"], "matters")
for row in payload["data"]:
print(row["decisionDate"], row["caseNumber"], row["title"])Following a matter from leave to appeal
A special leave petition under Article 136 asks the court for permission to appeal. If leave is granted, the registry renumbers the same dispute as a civil appeal or a criminal appeal. One controversy, two numbers, and the interlocutory orders scatter across both. Any tool that models an apex court matter as a single row will lose half of it.
The two endpoints that put the pieces back together are /cases/:id/related, which returns every document sharing a case number up to a ceiling of 50 along with a date ordered timeline, and /cases/:id/analyze-consolidated, which for an apex court matter reads up to 20 documents under that number and reasons over them together rather than one at a time. The consolidated route runs synchronously and consumes AI credits, so call it when you want the reasoning across a lineage rather than for bulk enrichment.
const BASE = "https://research.courtmesh.ai/api/v1/prod";
const headers = {
"X-API-Key": process.env.COURTMESH_API_KEY,
"Content-Type": "application/json",
};
// A Supreme Court matter accumulates orders under one case number.
// /related gathers every document that shares it and builds a timeline.
async function lineage(caseId) {
const res = await fetch(`${BASE}/cases/${caseId}/related`, { headers });
const { data, meta } = await res.json();
console.log(meta.caseNumber, "->", meta.totalDocuments, "documents");
data.timeline.forEach((event) => {
// status is one of: Case Initiated, Hearings / Orders, Final Judgment
console.log(event.date, event.status, event.statusLabel);
});
return data.relatedDocuments;
}
// Reason across the whole lineage rather than a single order.
async function consolidate(caseId) {
const res = await fetch(`${BASE}/cases/${caseId}/analyze-consolidated`, {
method: "POST",
headers,
body: JSON.stringify({ force: false }),
});
const { data } = await res.json();
return data.consolidatedAnalysis;
}Reportability and citations: what is not here
Apex court judgments circulate under several identities at once. The registry marks a judgment reportable or not, the official reports carry it in the Supreme Court Reports, the private reporters give it their own parallel citations, and since 2023 there has been a neutral citation running alongside all of them. A single decision can be cited four ways in four briefs.
The API does not model any of that. There is no citations array, no reportability flag, and no lookup from a reporter citation to a record. Parallel citations appear inside judgment text and, where a document has been analysed, inside the citedCases groupings that sort earlier authority into followed, distinguished, overruled and referred. If you need to resolve a citation, search it as a phrase in the query string and confirm against the document, which is what /cases/:id/pdf is for. That route returns an encrypted link valid for 3,600 seconds.
Two other honest boundaries. Cause lists and future listing dates are not exposed by this API at all, so nothing here will tell you when a matter is next before the court. And the semantic route searches the analysed subset rather than the whole corpus, so a niche order that has never been analysed will answer to keyword search and stay invisible to a vector query.
What teams build on the apex court layer
Constitutional research tools
Resolve a bench through the judge index, pull every matter it decided in a date window, then read the analysis object for doctrines applied and constitutional provisions engaged.
Precedent monitors
Watch a line of authority by re-running a semantic query on a schedule and diffing the case numbers that come back, then confirm each new arrival against the stored document.
Judge level analytics
Aggregate disposalNature and decisionDate across a judge's matters. Because judges is an array on every record, coram sized cohorts fall out of the same query.
Brief assembly
Take a lineage from the related endpoint, run consolidated analysis over it once, and hand a drafter the headnote, issues, holding and cited authority in one payload.
Supreme Court API questions
Is there a diaryNumber parameter I can filter on?
No. The search body accepts query, court, year, caseType, judgeName, fromDate, toDate, page, limit, sortBy and searchAfter, and nothing else. A diary number belongs in the query string, where the query parser recognises number and year patterns such as 12345/2023 and normalises them. Once you open a specific record, GET /cases/:id returns metadata.diaryNumber for documents where the registry number was captured, so the identifier is readable even though it is not filterable.
What is the difference between a diary number and a case number here?
A diary number is issued by the Supreme Court registry the moment a matter is filed, before scrutiny and registration. It stays with the filing through defect curing and is often the only identifier a pending matter has. A case number arrives after registration and encodes the case type, for example a special leave petition or a writ petition. Our records key on caseNumber, which is why the related endpoint groups documents by case number rather than by diary number.
How do I filter for a Constitution Bench matter?
There is no coram or bench strength parameter, so filter on the judges instead. Resolve each name through /judges/search, then pass judgeName with the names you care about, or read the judges array on each returned record and count it yourself. For matters that already carry analysis, the analysis object exposes procedureType and precedentValue, which are usually the fields that tell a reference apart from a routine appeal.
Which court value do I pass for the Supreme Court?
Exactly one string: Supreme Court of India. Unlike the High Courts, which appear in the index under several spellings, the apex court has a single canonical value, so a court filter of Supreme Court of India is complete on its own. You can also write it inline in the query as court:"Supreme Court of India", which the query parser lifts out before searching.
Does caseType let me ask for special leave petitions only?
Not at the apex court. The case type catalogue lists three values for the Supreme Court, namely Final Order, Judgment and Order, which describe the document rather than the proceeding. The special leave petition or appeal identity sits inside the case number text. Put it in the query string instead, for example SLP(C) followed by the number, and let the parser do the matching.
How does an SLP that becomes an appeal show up in the data?
As two identifiers. When leave is granted under Article 136 the registry renumbers the matter as a civil or criminal appeal, so the pre-leave and post-leave documents can carry different case numbers even though the dispute is one. Because /cases/:id/related groups on case number, it will show you the lineage under whichever number the documents were filed against. To bridge the two, search the party names or the impugned judgment reference in the query string.
Are AIR, SCC and neutral citations returned as fields?
No. There is no citations array in the response schema. What you get on a record is id, caseNumber, title, court, caseType, judges, petitioners, respondents, decisionDate, disposalNature, summary, hasDocuments, documentCount and hasAnalysis. Parallel citations that appear in the body of a judgment are reachable through the text and, where analysis exists, through the citedCases groupings of followed, distinguished, overruled and referred.
Can I tell whether a judgment was marked reportable?
Not through a dedicated flag. Reportability is a registry marking on the judgment itself rather than a field we expose. The closest signals in the API are hasAnalysis, which tells you whether the document has been read into structured analysis, and precedentValue inside the analysis object where it has been derived.
Why does semantic search return fewer Supreme Court results than keyword search?
The two run over different collections. Keyword search covers the full corpus of more than 310 million records. Semantic search runs over the vector index, which holds the subset of roughly two million judgments that have been analysed and embedded, and it drops anything scoring below 0.30 similarity. Its pagination totals are estimates rather than exact counts except on the final page. Treat it as a way to find reasoning you cannot phrase, not as a census.
What are the rate limits and how do I page through large result sets?
An API key is limited to 10 requests per minute. Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset, and a rejected call answers 429 with retryAfter in seconds. Page size caps at 100. For deep pagination pass the searchAfter cursor on /search/cases rather than walking page numbers, which keeps results stable while you iterate.
Keep reading
Full parameter reference, error codes and language snippets live in the developer documentation.