Guides · 24 October 2026
German court decisions for RAG: a practical guide
Germany’s federal courts publish their decisions for free. Turning them into something a legal assistant can search and cite takes a few deliberate choices. Here is what matters, from the source data to chunks with exact citations.
Where the decisions come from
All federal courts publish on rechtsprechung-im-internet.de, the case law portal of the Federal Ministry of Justice: the Federal Court of Justice (BGH), the Federal Constitutional Court (BVerfG), the Federal Labour Court (BAG), the Federal Fiscal Court (BFH), the Federal Social Court (BSG), the Federal Administrative Court (BVerwG) and the Federal Patent Court (BPatG). That is about 84,600 decisions since January 2010, with new ones every working day.
Court decisions are official works without copyright (§ 5 UrhG), and the courts anonymise them before publication, so you can use them freely. The portal offers a table of contents as XML and one ZIP file per decision. State courts publish elsewhere, each in its own way.
Chunk by section, not by length
A German decision follows a fixed structure: headnote (Leitsatz), operative part (Tenor), facts (Tatbestand) and reasons (Gründe), often with numbered headings inside the reasons. These parts do very different jobs. The facts tell what happened, the reasons tell why the court decided as it did.
A chunk that starts in the facts and ends in the reasons mixes the two, and retrieval suffers: a question about the legal reasoning pulls up half a page of facts. Keep every chunk inside one section, break between paragraphs, and store the heading path (for example “A. II.”) with the chunk so the model knows where it is.
Keep the marginal numbers
German lawyers cite decisions by marginal number (Randnummer), not by page: “BGH, IX ZB 72/08, Rn. 4–6”. If every chunk carries the court, the file number and its range of marginal numbers, your assistant can answer with a citation a lawyer can check in seconds. This one detail does more for trust in a legal assistant than any prompt.
Stay current
New decisions appear a few days after the decision date. For a daily update, compare the portal’s table of contents with what you already have, rather than relying on the timestamps alone: the portal sometimes re-stamps large numbers of older decisions at once, and those are not new case law.
Two smaller things save trouble later. Normalise umlauts on both sides before keyword filtering, because users type “Kuendigung” while the text says “Kündigung”. And keep the cited norms as their own field, so “all decisions on § 573 BGB” is a simple filter instead of a full-text search.
The short way: one API call
The German Court Decisions API does all of the above and returns ready-to-embed chunks. Each chunk stays inside one section and comes with court, file number, cited norms, heading path and marginal numbers:
{
"court": "BVerfG",
"decisionDate": "2026-10-14",
"fileNumber": "2 BvE 5/26",
"decisionType": "Einstweilige Anordnung",
"norms": ["Art 21 Abs 1 S 1 GG", "§ 32 Abs 1 BVerfGG"],
"section": "gruende",
"sectionLabel": "Gründe",
"headingPath": ["A.", "II."],
"marginalNumberFrom": 11,
"marginalNumberTo": 15,
"text": "[11] b) Sämtliche in dem Video thematisierten Themenkomplexe ...",
"chunkIndex": 5,
"chunkCount": 20
}
With the Apify Python client, a RAG pipeline for recent BGH decisions on residential tenancy termination looks like this:
from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("layerwiselabs/german-court-decisions").call(run_input={
"courts": ["BGH"],
"dateFrom": "2026-01-01",
"keywords": ["§ 573 BGB"],
"outputFormat": "chunks",
})
for chunk in client.dataset(run["defaultDatasetId"]).iterate_items():
citation = f'{chunk["court"]}, {chunk["fileNumber"]}, Rn. {chunk["marginalNumberFrom"]}–{chunk["marginalNumberTo"]}'
# hand each chunk to your embedding model and vector store
embed_and_store(chunk["text"], metadata={**chunk, "citation": citation})
Add a daily schedule with “only new since the last run” and the index stays current on its own. AI agents can also call it directly as a tool through the Apify MCP server: https://mcp.apify.com?tools=layerwiselabs/german-court-decisions
What it costs
$3 per 1,000 decisions, plus $0.02 per run. You pay per decision, not per chunk, so a decision split into 40 chunks costs the same as one without. Keyword and type filters add $0.0005 for each decision searched without a match. 100 BGH decisions as RAG chunks cost about $0.32, and a free Apify account includes $5 of credit every month.