API

The OLDP API is based on Django REST Framework and provides programmatic access to legal data including cases, laws, courts, and law books.

Base URL

All API requests should be made to:

https://de.openlegaldata.io/api/

Authentication

The API supports two authentication methods:

2. Session Authentication

For web-based clients, you can use standard Django session authentication with your username and password.

Permission System

API tokens use a fine-grained permission system that controls access to specific resources and actions.

Permission Levels

Permissions are defined by resource and action:

  • Resources: cases, laws, courts, lawbooks, references, annotations

  • Actions: read, write, delete

Default Permissions

By default, new API tokens are assigned to the “default” permission group which provides:

  • cases:read - Read access to cases

  • laws:read - Read access to laws

  • courts:read - Read access to courts

  • lawbooks:read - Read access to law books

This ensures secure, read-only access by default. Write and delete permissions must be explicitly granted by administrators.

Permission Groups

Administrators can create custom permission groups with specific combinations of permissions. Tokens are assigned to permission groups, making it easy to manage access for different use cases:

  • default: Read-only access to core resources

  • read_write: Read and write access to specific resources

  • full_access: Complete access including delete operations

Contact the administrators if you need elevated permissions for your API token.

Throttle Rates

To ensure fair usage and maintain service quality, the API implements rate limiting:

  • Anonymous users: 100 requests per day

  • Authenticated users: 5,000 requests per hour

If you need higher limits, please contact us or consider using our data dumps for bulk access.

API Endpoints

Cases

List all cases:

curl -X GET "https://de.openlegaldata.io/api/cases/" \
  -H "Authorization: Token YOUR_API_TOKEN_HERE" \
  -H "Accept: application/json"

Filter cases by court:

curl -X GET "https://de.openlegaldata.io/api/cases/?court_id=3" \
  -H "Authorization: Token YOUR_API_TOKEN_HERE" \
  -H "Accept: application/json"

Get a specific case:

curl -X GET "https://de.openlegaldata.io/api/cases/12345/" \
  -H "Authorization: Token YOUR_API_TOKEN_HERE" \
  -H "Accept: application/json"

Filter by date range:

curl -X GET "https://de.openlegaldata.io/api/cases/?date_after=2020-01-01&date_before=2023-12-31" \
  -H "Authorization: Token YOUR_API_TOKEN_HERE" \
  -H "Accept: application/json"

Laws

Field availability: the list endpoint (/api/laws/) returns summary records without the content field, mirroring how /api/cases/ behaves. Use the detail endpoint (/api/laws/<id>/) to retrieve a law section’s full HTML body. This keeps bulk pagination cheap on bandwidth and origin CPU; for whole-dataset access prefer the data dumps.

List all laws (no content):

curl -X GET "https://de.openlegaldata.io/api/laws/" \
  -H "Authorization: Token YOUR_API_TOKEN_HERE" \
  -H "Accept: application/json"

Filter laws by book:

curl -X GET "https://de.openlegaldata.io/api/laws/?book_id=5" \
  -H "Authorization: Token YOUR_API_TOKEN_HERE" \
  -H "Accept: application/json"

Get a specific law (includes content):

curl -X GET "https://de.openlegaldata.io/api/laws/123/" \
  -H "Authorization: Token YOUR_API_TOKEN_HERE" \
  -H "Accept: application/json"

Law Books

List all law books:

curl -X GET "https://de.openlegaldata.io/api/lawbooks/" \
  -H "Authorization: Token YOUR_API_TOKEN_HERE" \
  -H "Accept: application/json"

Get a specific law book:

curl -X GET "https://de.openlegaldata.io/api/lawbooks/bgb/" \
  -H "Authorization: Token YOUR_API_TOKEN_HERE" \
  -H "Accept: application/json"

Filter by code:

curl -X GET "https://de.openlegaldata.io/api/lawbooks/?code=BGB" \
  -H "Authorization: Token YOUR_API_TOKEN_HERE" \
  -H "Accept: application/json"

Courts

List all courts:

curl -X GET "https://de.openlegaldata.io/api/courts/" \
  -H "Authorization: Token YOUR_API_TOKEN_HERE" \
  -H "Accept: application/json"

Filter courts by type:

curl -X GET "https://de.openlegaldata.io/api/courts/?court_type=AG" \
  -H "Authorization: Token YOUR_API_TOKEN_HERE" \
  -H "Accept: application/json"

Get a specific court:

curl -X GET "https://de.openlegaldata.io/api/courts/ag-berlin/" \
  -H "Authorization: Token YOUR_API_TOKEN_HERE" \
  -H "Accept: application/json"

Pagination

API responses are paginated. Use the limit and offset parameters to navigate through results:

# Get first 50 results
curl -X GET "https://de.openlegaldata.io/api/cases/?limit=50&offset=0" \
  -H "Authorization: Token YOUR_API_TOKEN_HERE" \
  -H "Accept: application/json"

# Get next 50 results
curl -X GET "https://de.openlegaldata.io/api/cases/?limit=50&offset=50" \
  -H "Authorization: Token YOUR_API_TOKEN_HERE" \
  -H "Accept: application/json"

The response includes pagination metadata:

{
  "count": 1234,
  "next": "https://de.openlegaldata.io/api/cases/?limit=50&offset=50",
  "previous": null,
  "results": [...]
}

Citations & Cross-References

The citation graph is queryable in three complementary ways. The same data is also available via the MCP server — the REST nested actions and the MCP tools share a single service layer, so payload shapes match across the two agent-facing surfaces. The human-facing web search at /search/?cited_law_book=…&cited_law_section=… (or ?cited_case=<id>) renders the same Elasticsearch-backed result set with facets and pagination.

Nested actions on cases & laws

Map 1:1 to the natural questions about a single case or law section.

Endpoint

Returns

Backend

GET /api/cases/<id>/references/

Forward refs emitted by this case (laws + cases it cites)

SQL

GET /api/cases/<id>/citing_cases/

Cases whose body cites this case

Elasticsearch

GET /api/cases/<id>/citing_laws/

Laws whose body cites this case

SQL

GET /api/laws/<id>/references/

Forward refs emitted by this law

SQL

GET /api/laws/<id>/citing_cases/

Cases whose body cites this law section

Elasticsearch

GET /api/laws/<id>/citing_laws/

Laws whose body cites this law section

SQL

The references/ endpoints return a single dict (total_law_references, total_case_references, law_references[], case_references[], references_extracted_at). The citing_* endpoints return a paginated list of summary records — content is omitted on these list-style responses for both cases and laws; fetch the detail endpoint when the body HTML is actually needed.

For laws, the citing-cases lookup is keyed by (book_slug, section_slug) against the CaseIndex.cited_laws field. The slug pair is stable across book revisions, so older citation rows pinned to non-latest revisions still surface — no (book_code, section) sibling expansion needed at query time.

Elasticsearch dependency on citing-cases endpoints

/api/cases/<id>/citing_cases/ and /api/laws/<id>/citing_cases/ read from Elasticsearch (CaseIndex.cited_cases and CaseIndex.cited_laws respectively). When ES is unavailable these endpoints return 503 with a structured body so clients can differentiate transient warm-up from a hard outage:

// 503 — transient timeout, agent should retry
{
  "detail": "Search timed out while warming caches. Retry the same query in a few seconds.",
  "code": "search_backend_timeout",
  "retryable": true,
  "hint": "First-touch queries on large result sets read ES segments from disk; the same query is sub-100ms on the next attempt."
}

// 503 — hard outage
{
  "detail": "Search backend is currently unavailable. Please try again later.",
  "code": "search_backend_unavailable"
}

See docs/elasticsearch.md for the underlying index fields and the reindex command an operator must run after upgrading a release that changes either field’s shape.

# What does case 12345 cite?
curl -X GET "https://de.openlegaldata.io/api/cases/12345/references/" \
  -H "Authorization: Token $OLDP_API_TOKEN"

# Which cases cite § 823 BGB?
curl -X GET "https://de.openlegaldata.io/api/laws/<law-id>/citing_cases/" \
  -H "Authorization: Token $OLDP_API_TOKEN"

Flat /api/references/

For cross-cutting queries the nested actions can’t express. Filter by either numeric IDs or slugs (no id round-trip required):

Filter

Field

cited_by_case=<id>

the source case whose body emitted the cite

cited_by_case__slug=<case_slug>

same, by slug

cited_by_law=<id>

source law (id)

cited_by_law__slug=<section_slug>

source law section slug

cited_by_law__book__slug=<book_slug>

source law book slug

cites_case=<id>

target case (id)

cites_case__slug=<case_slug>

target case (slug)

cites_law=<id>

target law (id)

cites_law__slug=<section_slug>

target law section slug

cites_law__book__slug=<book_slug>

target law book slug

`assigned=true

false`

Filters compose. Examples:

# Every reference involving § 823 BGB as the target, by slug
curl -X GET "https://de.openlegaldata.io/api/references/?cites_law__book__slug=bgb&cites_law__slug=823" \
  -H "Authorization: Token $OLDP_API_TOKEN"

# Refs emitted by laws within the BGB book pointing at any law
curl -X GET "https://de.openlegaldata.io/api/references/?cited_by_law__book__slug=bgb&cites_law__isnull=False" \
  -H "Authorization: Token $OLDP_API_TOKEN"

# Refs from a specific case (slug) to assigned targets only
curl -X GET "https://de.openlegaldata.io/api/references/?cited_by_case__slug=bgh-vi-zr-123-22&assigned=true" \
  -H "Authorization: Token $OLDP_API_TOKEN"

Each row carries the source (cited_by), the target (case or law), the marker text (the literal citation as it appeared in the source), and the unresolved free-form to field used during extraction.

Citation validation

GET /api/citations/validate/?citation=...&type=... checks whether a free-form German legal citation (Aktenzeichen, ECLI, or paragraph reference) exists in the local DB.

curl -G "https://de.openlegaldata.io/api/citations/validate/" \
  --data-urlencode 'citation=§ 823 BGB' \
  -H "Authorization: Token $OLDP_API_TOKEN"
# {"found": true, "type": "law", "matches": [...]}

curl -G "https://de.openlegaldata.io/api/citations/validate/" \
  --data-urlencode 'citation=VI ZR 123/22' \
  -H "Authorization: Token $OLDP_API_TOKEN"
# {"found": true, "type": "case", "matches": [...]}

type defaults to auto (sniff from the input shape). Force a specific parse with type=file_number, type=ecli, or type=law_reference.

My Resources (/me/)

The /me/ endpoints let you view resources you have created with your API token. This is useful for tracking submissions and checking their review status.

For detailed documentation, see My Resources API.

List your cases:

curl -X GET "https://de.openlegaldata.io/api/me/cases/" \
  -H "Authorization: Token YOUR_API_TOKEN_HERE" \
  -H "Accept: application/json"

List your law books:

curl -X GET "https://de.openlegaldata.io/api/me/law_books/" \
  -H "Authorization: Token YOUR_API_TOKEN_HERE"

User profile and token info:

curl -X GET "https://de.openlegaldata.io/api/me/" \
  -H "Authorization: Token YOUR_API_TOKEN_HERE"

Creating and Updating Resources

Note: Write operations require a token with appropriate write permissions.

Case Creation API

For detailed documentation on creating cases programmatically, including automatic court resolution, duplicate handling, and reference extraction, see the Case Creation API Documentation.

Create a new case (requires cases:write permission):

curl -X POST "https://de.openlegaldata.io/api/cases/?extract_refs=true" \
  -H "Authorization: Token YOUR_API_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "court_name": "Bundesgerichtshof",
    "file_number": "I ZR 123/21",
    "date": "2021-05-15",
    "content": "<p>Full case content in HTML...</p>",
    "type": "Urteil"
  }'

The API automatically resolves the court from the court_name field. Use ?extract_refs=true (default) to extract legal references from the content, or ?extract_refs=false to disable.

Update an existing case (requires cases:write permission):

curl -X PATCH "https://de.openlegaldata.io/api/cases/12345/" \
  -H "Authorization: Token YOUR_API_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "title": "Updated Case Title"
  }'

Court Creation API

For detailed documentation on creating courts programmatically, including automatic state/city resolution, duplicate handling, and review workflow, see the Court Creation API Documentation.

Create a new court (requires courts:write permission):

curl -X POST "https://de.openlegaldata.io/api/courts/" \
  -H "Authorization: Token YOUR_API_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "name": "Amtsgericht Berlin-Mitte",
    "code": "AGBERLINMITTE",
    "state_name": "Berlin",
    "court_type": "AG",
    "city_name": "Berlin"
  }'

The API automatically resolves the state and city from their names. Courts created via API are set to review_status="pending" until approved by an administrator.

Delete a resource (requires appropriate delete permission):

curl -X DELETE "https://de.openlegaldata.io/api/cases/12345/" \
  -H "Authorization: Token YOUR_API_TOKEN_HERE" \
  -H "Accept: application/json"

Error Handling

The API uses standard HTTP status codes:

  • 200 OK: Request successful

  • 201 Created: Resource created successfully

  • 400 Bad Request: Invalid request parameters

  • 401 Unauthorized: Missing or invalid authentication

  • 403 Forbidden: Insufficient permissions

  • 404 Not Found: Resource not found

  • 429 Too Many Requests: Rate limit exceeded

  • 500 Internal Server Error: Server error

Example error response:

{
  "detail": "Authentication credentials were not provided."
}

Permission denied response:

{
  "detail": "You do not have permission to perform this action."
}

Response Formats

The API supports multiple response formats via the Accept header:

  • JSON (default): Accept: application/json

  • XML: Accept: application/xml

  • Browsable API: Accept: text/html (for web browsers)

Best Practices

  1. Use HTTPS: Always use HTTPS to protect your API token

  2. Store tokens securely: Never commit tokens to version control

  3. Use environment variables: Store your token in environment variables:

    export OLDP_API_TOKEN="your_token_here"
    curl -H "Authorization: Token $OLDP_API_TOKEN" ...
    
  4. Handle rate limits: Implement exponential backoff when you receive 429 responses

  5. Use pagination: Don’t fetch all results at once; use pagination for large datasets

  6. Monitor token usage: Check your token’s last used timestamp in your account settings

  7. Rotate tokens regularly: Create new tokens periodically and revoke old ones

  8. Use specific permissions: Request only the permissions you need for your use case

Examples

Complete Example: Fetching Cases from a Specific Court

#!/bin/bash

# Set your API token
export OLDP_API_TOKEN="your_token_here"
export BASE_URL="https://de.openlegaldata.io/api"

# 1. Find the court ID
echo "Finding court..."
COURT_ID=$(curl -s -X GET "$BASE_URL/courts/?code=BGH" \
  -H "Authorization: Token $OLDP_API_TOKEN" \
  -H "Accept: application/json" | jq -r '.results[0].id')

echo "Court ID: $COURT_ID"

# 2. Fetch cases from this court
echo "Fetching cases..."
curl -s -X GET "$BASE_URL/cases/?court_id=$COURT_ID&limit=10" \
  -H "Authorization: Token $OLDP_API_TOKEN" \
  -H "Accept: application/json" | jq '.results[] | {title, date, file_number}'

Example: Exporting Data to CSV

#!/bin/bash

export OLDP_API_TOKEN="your_token_here"
export BASE_URL="https://de.openlegaldata.io/api"

# Fetch and convert to CSV
curl -s -X GET "$BASE_URL/cases/?limit=100" \
  -H "Authorization: Token $OLDP_API_TOKEN" \
  -H "Accept: application/json" | \
  jq -r '.results[] | [.id, .title, .date, .court.name] | @csv' > cases.csv

echo "Exported to cases.csv"

Data Dumps and Bulk Downloads

For bulk access, prefer the dump_api_data management command over making thousands of API requests. It produces gzipped JSONL files plus a snapshot manifest. See Data Dumps & Bulk Downloads for full details.