Cross-Border Case Law Comparison
Use semantic search to find functionally equivalent case law across multiple European jurisdictions for comparative legal analysis.
What you'll build
Comparative law research is one of the most time-consuming tasks in cross-border legal practice. A question like "How do different EU member states handle the right to be forgotten?" requires searching multiple national court databases, each with its own language, citation format, and search interface. The Moonlit API's semantic search makes this dramatically easier. Because it searches by meaning rather than keywords, you can use a single natural-language query to find functionally equivalent rulings across jurisdictions -- even when the legal terminology differs between Dutch, German, French, and English-language courts. This tutorial builds a Python tool that takes a legal concept, searches across multiple jurisdictions simultaneously, groups results by country, and produces a structured comparison table showing how each jurisdiction approaches the same legal issue.
Architecture
┌────────────────────┐
│ Legal Concept │
│ (natural lang.) │
└─────────┬──────────┘
│
┌──────────┼──────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ NL │ │ EU │ │ DE │
│ semantic│ │ semantic│ │ semantic│
│ search │ │ search │ │ search │
└────┬────┘ └────┬────┘ └────┬────┘
│ │ │
└──────────┼──────────┘
▼
┌──────────────────────────┐
│ Comparison Table │
│ grouped by jurisdiction│
└──────────────────────────┘Prerequisites
- A Moonlit API key
- Python 3.10+
- pip install requests tabulate
Step-by-step
Define the comparison query
Write a natural-language description of the legal concept you want to compare. Semantic search works best with complete questions or descriptions rather than keyword fragments.
import requests
from collections import defaultdict
MOONLIT_KEY = "your-api-key"
BASE_URL = "https://api.moonlit.ai/v1.1"
QUERY = "What standard of proof is required for interim injunctions in intellectual property disputes?"
JURISDICTIONS = ["Netherlands", "Germany", "European Union", "Belgium"]Search each jurisdiction
Run a semantic reranked search for each jurisdiction separately. This ensures you get the top results from each country rather than having one jurisdiction dominate the combined results.
def search_jurisdiction(query: str, jurisdiction: str) -> list[dict]:
response = requests.post(
f"{BASE_URL}/search/semantic_search",
headers={
"Ocp-Apim-Subscription-Key": MOONLIT_KEY,
"Content-Type": "application/json",
},
json={
"query": query,
"jurisdictions": [jurisdiction],
"documentTypes": ["case_law"],
"num_results": 5,
},
)
response.raise_for_status()
return response.json()["result"]["results"]
# Collect results by jurisdiction
results_by_jurisdiction = {}
for jur in JURISDICTIONS:
results = search_jurisdiction(QUERY, jur)
results_by_jurisdiction[jur] = results
print(f"{jur}: {len(results)} results found")Build the comparison table
Structure the results into a comparison format showing jurisdiction, court level, key ruling, and ECLI identifier side by side.
from tabulate import tabulate
rows = []
for jur, results in results_by_jurisdiction.items():
for r in results[:3]: # Top 3 per jurisdiction
rows.append([
jur,
r.get("source", "N/A"),
r["date"],
r["identifier"],
(r.get("summary", "")[:120] + "...") if r.get("summary") else "N/A",
])
print(tabulate(
rows,
headers=["Jurisdiction", "Court", "Date", "ECLI", "Key holding"],
tablefmt="grid",
))Retrieve full texts for deep comparison
For the most relevant result from each jurisdiction, retrieve the full document text. These can then be passed to an LLM to generate a structured comparative analysis.
def get_top_documents(results_by_jur: dict) -> dict:
"""Retrieve the top result's full text from each jurisdiction."""
docs = {}
for jur, results in results_by_jur.items():
if results:
top = results[0]
response = requests.get(
f"{BASE_URL}/document/retrieve_document",
headers={"Ocp-Apim-Subscription-Key": MOONLIT_KEY},
params={"DocumentIdentifier": top["identifier"]},
)
if response.ok:
docs[jur] = response.json()
return docs
top_docs = get_top_documents(results_by_jurisdiction)
for jur, doc in top_docs.items():
print(f"\n{'='*60}")
print(f"{jur}: {doc['title']}")
print(f"Court: {doc.get('source', 'N/A')}")
print(f"Summary: {doc.get('summary', 'N/A')[:300]}")Complete Code
#!/usr/bin/env python3
"""Cross-border case law comparison using Moonlit semantic search."""
import os
import requests
from tabulate import tabulate
MOONLIT_KEY = os.environ["MOONLIT_API_KEY"]
BASE_URL = "https://api.moonlit.ai/v1.1"
HEADERS = {
"Ocp-Apim-Subscription-Key": MOONLIT_KEY,
"Content-Type": "application/json",
}
def search_jurisdiction(query: str, jurisdiction: str, n: int = 5) -> list[dict]:
response = requests.post(
f"{BASE_URL}/search/semantic_search",
headers=HEADERS,
json={
"query": query,
"jurisdictions": [jurisdiction],
"documentTypes": ["case_law"],
"num_results": n,
},
)
response.raise_for_status()
return response.json()["result"]["results"]
def retrieve_document(doc_id: str) -> dict:
response = requests.get(
f"{BASE_URL}/document/retrieve_document",
headers={"Ocp-Apim-Subscription-Key": MOONLIT_KEY},
params={"DocumentIdentifier": doc_id},
)
response.raise_for_status()
return response.json()
def compare(query: str, jurisdictions: list[str]):
print(f"Query: {query}")
print(f"Jurisdictions: {', '.join(jurisdictions)}\n")
all_results = {}
for jur in jurisdictions:
results = search_jurisdiction(query, jur)
all_results[jur] = results
print(f" {jur}: {len(results)} results")
rows = []
for jur, results in all_results.items():
for r in results[:3]:
rows.append([
jur,
r.get("source", "N/A"),
r["date"],
r["identifier"],
(r.get("summary", "")[:100] + "...") if r.get("summary") else "N/A",
])
print("\n" + tabulate(rows, headers=["Jur.", "Court", "Date", "ECLI", "Key holding"], tablefmt="grid"))
print("\n\nDetailed comparison of top results:")
for jur, results in all_results.items():
if results:
doc = retrieve_document(results[0]["identifier"])
print(f"\n{'='*60}")
print(f"[{jur}] {doc['title']}")
print(f"Court: {doc.get('source', 'N/A')} | Date: {doc.get('date', 'N/A')}")
print(f"Summary: {doc.get('summary', 'N/A')[:400]}")
if __name__ == "__main__":
compare(
query="What standard of proof is required for interim injunctions in intellectual property disputes?",
jurisdictions=["Netherlands", "Germany", "European Union", "Belgium"],
)