# API The OLDP API is based on [Django REST Framework](http://www.django-rest-framework.org/) 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: ### 1. API Tokens (Recommended) API tokens provide secure, fine-grained access control to API resources. You can create and manage multiple tokens with different permission levels in your account settings. **How to get your API token:** 1. Log in to your account 2. Navigate to your profile settings 3. Go to the "API Access" section 4. Create a new token with a descriptive name 5. Copy the token (it will only be shown once!) **Using your token in requests:** Add the token to the `Authorization` header using the `Token` prefix: ```bash curl -X GET "https://de.openlegaldata.io/api/cases/" \ -H "Authorization: Token YOUR_API_TOKEN_HERE" \ -H "Accept: application/json" ``` ### 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:** ```bash curl -X GET "https://de.openlegaldata.io/api/cases/" \ -H "Authorization: Token YOUR_API_TOKEN_HERE" \ -H "Accept: application/json" ``` **Filter cases by court:** ```bash 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:** ```bash 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:** ```bash 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//`) 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](../data-dumps.md). **List all laws (no `content`):** ```bash curl -X GET "https://de.openlegaldata.io/api/laws/" \ -H "Authorization: Token YOUR_API_TOKEN_HERE" \ -H "Accept: application/json" ``` **Filter laws by book:** ```bash 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`):** ```bash 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:** ```bash 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:** ```bash curl -X GET "https://de.openlegaldata.io/api/lawbooks/bgb/" \ -H "Authorization: Token YOUR_API_TOKEN_HERE" \ -H "Accept: application/json" ``` **Filter by code:** ```bash 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:** ```bash curl -X GET "https://de.openlegaldata.io/api/courts/" \ -H "Authorization: Token YOUR_API_TOKEN_HERE" \ -H "Accept: application/json" ``` **Filter courts by type:** ```bash 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:** ```bash 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: ```bash # 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: ```json { "count": 1234, "next": "https://de.openlegaldata.io/api/cases/?limit=50&offset=50", "previous": null, "results": [...] } ``` ## Search **Search cases by text:** ```bash curl -X GET "https://de.openlegaldata.io/api/cases/search/?text=urheberrecht" \ -H "Authorization: Token YOUR_API_TOKEN_HERE" \ -H "Accept: application/json" ``` **Search laws:** ```bash curl -X GET "https://de.openlegaldata.io/api/laws/search/?text=vertragsrecht" \ -H "Authorization: Token YOUR_API_TOKEN_HERE" \ -H "Accept: application/json" ``` Search supports Lucene syntax for complex queries: ```bash curl -X GET "https://de.openlegaldata.io/api/cases/search/?text=urheberrecht+AND+software" \ -H "Authorization: Token YOUR_API_TOKEN_HERE" \ -H "Accept: application/json" ``` `text` is required. Optional filters that compose with the keyword query (logical AND): | Param | Notes | |-------|-------| | `start_date`, `end_date` | `YYYY-MM-DD`, inclusive | | `cited_law_book` + `cited_law_section` | Restrict to cases citing a specific section (case search only — silently ignored on `/api/laws/search/`) | | `cited_case` | Restrict to cases citing the case with this id | | `return_text=1` | Include the full `text` alongside `snippets` | ```bash # Cases citing § 823 BGB that mention "Mietrecht" curl -G "https://de.openlegaldata.io/api/cases/search/" \ --data-urlencode 'text=Mietrecht' \ --data-urlencode 'cited_law_book=bgb' \ --data-urlencode 'cited_law_section=823' \ -H "Authorization: Token $OLDP_API_TOKEN" ``` See [Search](../searching.md) for the full filter matrix across the web, REST, and MCP surfaces. ## Citations & Cross-References The citation graph is queryable in three complementary ways. The same data is also available via the [MCP server](../mcp.md) — 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=`) 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//references/` | Forward refs emitted by this case (laws + cases it cites) | SQL | | `GET /api/cases//citing_cases/` | Cases whose body cites this case | Elasticsearch | | `GET /api/cases//citing_laws/` | Laws whose body cites this case | SQL | | `GET /api/laws//references/` | Forward refs emitted by this law | SQL | | `GET /api/laws//citing_cases/` | Cases whose body cites this law section | Elasticsearch | | `GET /api/laws//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//citing_cases/` and `/api/laws//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: ```json // 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](../elasticsearch.md#index-fields-driving-citation-lookups) for the underlying index fields and the reindex command an operator must run after upgrading a release that changes either field's shape. ```bash # 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//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=` | the source case whose body emitted the cite | | `cited_by_case__slug=` | same, by slug | | `cited_by_law=` | source law (id) | | `cited_by_law__slug=` | source law section slug | | `cited_by_law__book__slug=` | source law book slug | | `cites_case=` | target case (id) | | `cites_case__slug=` | target case (slug) | | `cites_law=` | target law (id) | | `cites_law__slug=` | target law section slug | | `cites_law__book__slug=` | target law book slug | | `assigned=true|false` | drop unresolved refs (no `case`/`law` FK) | Filters compose. Examples: ```bash # 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. ```bash 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](me-endpoints.md). **List your cases:** ```bash 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:** ```bash curl -X GET "https://de.openlegaldata.io/api/me/law_books/" \ -H "Authorization: Token YOUR_API_TOKEN_HERE" ``` **User profile and token info:** ```bash 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](case-creation.md). **Create a new case (requires cases:write permission):** ```bash 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": "

Full case content in HTML...

", "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):** ```bash 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](court-creation.md). **Create a new court (requires courts:write permission):** ```bash 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):** ```bash 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:** ```json { "detail": "Authentication credentials were not provided." } ``` **Permission denied response:** ```json { "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: ```bash 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 ```bash #!/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 ```bash #!/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](../data-dumps.md) for full details.