Search is the part of a court data integration that gets demoed. Documents are the part that gets supported. Six months after launch, the tickets are almost never about relevance. They are a lawyer saying the link does not work, a junior saying the file downloaded but will not open, and an operations person asking why a batch of two thousand judgments came back with eleven hundred files.
None of those are exotic failures. They are the predictable consequences of treating a document endpoint like an address book. A judgment PDF over an API is not a permanent URL sitting in a field. It is a short lived credential issued on request, pointing at an object that may not exist for every record, delivered through a chain where several things can go wrong quietly.
This article covers what GET /cases/:id/pdf on https://research.courtmesh.ai/api/v1/prod actually returns, why it is shaped that way, and the handling that separates an integration which keeps working from one that degrades for a year before anybody measures it. The theme throughout is provenance: knowing what you fetched, when, and for which case.
What the Endpoint Returns
A successful call returns the standard envelope with a data object carrying five things: pdfUrl, expiresIn, caseId, caseNumber and caseTitle. The pdfUrl is an encrypted presigned link. The expiresIn is 3600, meaning the link is valid for one hour from the moment it was issued. The three case fields are there so that whatever you do next can record which record the file belongs to without a second lookup.
The path parameter behaves like the other case endpoints: it accepts either the internal case id you received from a search result or a case number. If the record does not exist you get 404 Case not found. If the record exists but has no stored document you get 404 with PDF not available for this case, and this second case is far more common than teams expect. It is not an error in your code and it is not a gap to retry. Some records in any court corpus simply never had a document published against them.
The one sentence that prevents most bugs
A presigned link is a credential, not an address. It is issued to you, it expires in one hour, and it is meaningless afterwards. Everything that goes wrong with document handling in court data integrations follows from somebody storing one in a database column called pdf_url and treating it as permanent.
Why the Link Expires, and Why That Is the Right Design
The instinctive developer reaction to an expiring link is mild irritation. A permanent URL would be simpler to store and simpler to render. It would also be a serious problem, and it is worth understanding why so that you build with the grain rather than against it.
A permanent public URL to a document store is a permanent public URL. Anyone who obtains it, from a forwarded email, a shared spreadsheet, a browser history, a support ticket screenshot or a log file, has the document forever, with no record of who fetched it and no way to withdraw access. A short lived signed link inverts all of that. Access is granted per request, attributable to the key that asked, and self revoking. If a link leaks, it leaks for an hour.
The same reasoning explains the watermarking you will see on text delivered through the API. Case detail and analysis responses carry a per key watermark on text fields, so that if content surfaces somewhere it should not, the leak can be traced back to a key rather than merely noticed. It is a tracing mechanism, not a restriction on legitimate use, and it exists because a corpus assembled at real cost from official government portals is worth a great deal to somebody willing to redistribute it.
| Design choice | The naive alternative | What the naive version costs you |
|---|---|---|
| Link expires in 3600 seconds | A permanent URL stored on the case record | Anyone who ever sees the URL keeps access indefinitely, with no attribution and no revocation. Also guarantees a support queue full of dead links six months later. |
| Link issued per request | A link generated once at ingestion time | Your stored links start expiring the hour you write them, and every one of them fails at the worst possible moment, which is when a partner clicks during a hearing. |
| 404 when no document exists | An empty string or a placeholder file | A placeholder is worse than nothing, because it is indistinguishable from a real file until somebody opens it. An explicit 404 lets you render an honest empty state. |
| Watermarked text in responses | Clean text with no provenance marking | No way to trace redistribution back to a source. The economics of maintaining an official source corpus depend on this being possible. |
The Correct Flow, and the Three Wrong Ones
There is one right shape for this and it is almost embarrassingly simple. The wrong shapes are all attempts to be clever about caching.
Fetch on intent, never in advance
The call happens when a user clicks a document, or when a job that is about to consume the file runs. It does not happen during search result rendering, during a nightly sync, or during ingestion. There is no benefit to holding a link you are not about to use, and there is a guaranteed cost when it expires.
Use it immediately and forget it
Your server receives the pdfUrl, either redirects the user to it or streams the bytes through, and then discards it. It does not go into a database, a cache, a session, an email, a chat message or a log line. If you must log the event, log the caseId, not the link.
Verify what you actually received
Before you treat the response as a judgment, check that it is one. Confirm the content type, confirm the file begins with a PDF header, and confirm the size is plausible rather than a few hundred bytes of error page. An error rendered as HTML and saved with a .pdf extension is the single most common corrupt file in this domain, and it fails silently until a human opens it.
Record provenance with the bytes
Whatever you do with the file, store four things next to it: the caseId it came from, the caseNumber and caseTitle the API returned, the retrieval timestamp, and a hash of the file. Those four fields answer every question anybody will later ask about the document, including whether the copy you hold is the copy you fetched.
Handle absence as a first class outcome
A 404 with PDF not available for this case is data, not failure. Record it against the case so you do not retry it nightly forever, and render an interface state that says no document has been published for this record rather than an error banner that implies your system is broken.
The wrong shapes are worth naming because each of them looks reasonable when written. Fetching all links at ingestion and storing them, which produces a database of credentials that expire within the hour. Caching the link keyed on case id with a long time to live, which produces intermittent failures that correlate with nothing and are therefore very hard to diagnose. And putting the raw link into a client side response so the browser fetches it directly, which is defensible for the redirect itself but becomes a leak the moment the link appears in a shared URL, a bookmark, or a referrer header.
If You Do Store Copies, Store Them Properly
Plenty of legitimate products keep local copies of judgments: a matter file needs its documents attached, an analysis pipeline needs the text, a firm wants its own archive of what it relied on. That is fine. What is not fine is doing it without a policy, because a document store full of court judgments is a data asset with obligations attached.
- Deduplicate on content, not on name. Hash the bytes. The same order can arrive under different case ids in a family of related documents, and a store keyed on filename will hold five copies of one thing and miss that a sixth is different.
- Keep the link between file and case permanently. A judgment PDF with no recorded case id is an orphan within a year. Nobody will re-derive it from the text, and it will be re-fetched.
- Restrict access the way you restrict the matter. A judgment is a public record, but the fact that your firm pulled these particular judgments is not public and is close to strategy. Access control on the document store should follow the matter, not be open to the whole organisation because the underlying record is public.
- Set a retention policy and actually apply it. Under the DPDP Act 2023 your organisation is a data fiduciary in its own right, and court documents contain personal data of parties and witnesses. Holding everything forever because storage is cheap is a decision, and it should be a deliberate one rather than a default.
- Do not redistribute in bulk. Serving a document to the user who asked for it is one thing. Republishing a corpus you assembled through an API is another, and it is a question for your counsel and your contract before it is a question for your engineering team.
Provenance, and the Reason It Matters More Here
In most domains a file is a file. In litigation, a document's history is sometimes the point. A judgment copy that will be placed before a court, annexed to a petition, or relied on in a diligence report is not merely content, it is something whose source somebody may have to account for.
Indian law has been unusually attentive to this for electronic material. The regime around section 65B of the Indian Evidence Act 1872, now carried forward in the Bharatiya Sakshya Adhiniyam 2023, developed precisely because a printout of an electronic record is a copy whose provenance is not self evident from the paper. The case law on when a certificate is required and who can give it has moved more than once, and it is not settled by anything an API does.
What an API does not do for you
Fetching a judgment over an API does not certify it, does not authenticate it, and does not discharge any evidentiary requirement. It gives you a copy of a document sourced from official portals, with a record of when you got it. A certified copy from the registry is a certified copy from the registry, and nothing else substitutes for it. Build your product so that the difference is visible to the user rather than blurred.
The practical consequence for a developer is modest and worth doing. Keep the retrieval timestamp. Keep the case id, number and title as returned. Keep a hash. Show the user, on the document view, where the copy came from and when it was fetched. None of that makes the copy certified, but it means that when somebody asks where this came from, the answer exists and is precise rather than reconstructed from memory six months later.
A document with no recorded provenance is not a smaller version of a document with provenance. In litigation it is a different kind of object, and the difference shows up at the worst possible time.
Batch Retrieval Without Breaking Anything
Bulk document pulls are where the rate limit and the expiry interact, and the interaction is the thing to design for. An API key is allowed ten requests per minute. A presigned link is valid for an hour. If you request two thousand links and then start downloading, the arithmetic works out badly: at ten requests a minute the request phase alone takes over three hours, and the earliest links died two hours in.
The fix is to keep request and download adjacent rather than separating them into phases. Treat one document as one unit of work: request the link, download the bytes, verify, store, record, mark done. Then the expiry window is never a factor, because it is measured in seconds rather than hours. Checkpoint after every item so that a crash costs one document, and make the unit idempotent so re-running it is harmless.
One item, one unit of work
Request, download, verify, store, mark complete. Never a phase that collects links and a later phase that consumes them. This single decision eliminates every expiry related bug in bulk retrieval.
Budget the throughput honestly
At ten requests per minute, and one request per document, a thousand documents is a hundred minutes at best. Plan the job against the calendar and run it in a durable queue that can be paused and resumed rather than a script somebody has to babysit.
Record absence as an outcome
Many records will return PDF not available for this case. Write that result down. A job that treats absence as a retryable failure will spend its entire budget re-asking for documents that do not exist.
Verify before you count it done
Content type, PDF header, plausible size, hash recorded. A run that reports success while writing error pages to disk is worse than a run that failed loudly, because nobody will look at it again.
There is nothing sophisticated in any of this, which is exactly why it is worth writing down. Document handling is where court data integrations quietly degrade, not because the problem is hard but because it is boring, and boring work gets deferred until it is a support queue. Build the unglamorous version first: fetch on intent, verify what arrives, record where it came from, and treat absence as an answer. The endpoint reference is here and the coverage behind it at the API overview.
Treat every document link as a one hour credential
GET /cases/:id/pdf on the CourtMesh API returns an encrypted presigned link valid for 3600 seconds, alongside the caseId, caseNumber and caseTitle so you can record provenance with the bytes. Records without a stored document return an explicit 404 rather than a placeholder, so your interface can be honest about what exists. Read the endpoint reference, see the corpus and court coverage at the API overview, and check what each call costs at API pricing.
Explore CourtMesh


