Relationship Expansion Documentation¶
The Danish Parliament API's $expand parameter allows you to include related data in a single request, dramatically reducing the number of API calls needed. This guide covers all expansion patterns, performance implications, and junction table strategies.
$expand Syntax Overview¶
The $expand parameter follows OData 3.0 expansion syntax:
Basic Expansion Patterns¶
| Pattern | Purpose | Example |
|---|---|---|
| Single relationship | Include one related entity | $expand=Sagskategori |
| Multiple relationships | Include several related entities | $expand=Sagskategori,Sagstrin |
| Nested expansion | Multi-level relationship traversal | $expand=Stemme/Aktør |
| Complex nested | Multiple nested relationships | $expand=SagAktør/Aktør,SagDokument/Dokument |
URL Encoding Required
Always use %24expand instead of $expand in URLs:
Single Relationship Expansion¶
Core Entity Expansions¶
Sag (Cases) Expansions¶
# Cases with their categories
curl "https://oda.ft.dk/api/Sag?%24expand=Sagskategori&%24top=3"
# Cases with their status information
curl "https://oda.ft.dk/api/Sag?%24expand=Sagsstatus&%24top=3"
# Cases with their type information
curl "https://oda.ft.dk/api/Sag?%24expand=Sagstype&%24top=3"
# Cases with parliamentary period
curl "https://oda.ft.dk/api/Sag?%24expand=Periode&%24top=3"
Aktør (Actors) Expansions¶
# Actors with their type information
curl "https://oda.ft.dk/api/Aktør?%24expand=Aktørtype&%24top=5"
Stemme (Votes) Expansions¶
# Individual votes with voting session details
curl "https://oda.ft.dk/api/Stemme?%24expand=Afstemning&%24top=10"
# Individual votes with politician details
curl "https://oda.ft.dk/api/Stemme?%24expand=Aktør&%24top=10"
# Individual votes with vote type
curl "https://oda.ft.dk/api/Stemme?%24expand=Stemmetype&%24top=10"
Dokument (Documents) Expansions¶
# Documents with their type
curl "https://oda.ft.dk/api/Dokument?%24expand=Dokumenttype&%24top=5"
# Documents with attached files
curl "https://oda.ft.dk/api/Dokument?%24expand=Fil&%24top=5"
# Documents with their status
curl "https://oda.ft.dk/api/Dokument?%24expand=Dokumentstatus&%24top=5"
Multiple Relationship Expansion¶
Expand several relationships in one request using comma separation:
Common Multi-Expansion Patterns¶
# Cases with category and status
curl "https://oda.ft.dk/api/Sag?%24expand=Sagskategori,Sagsstatus&%24top=3"
# Cases with category, status, and type
curl "https://oda.ft.dk/api/Sag?%24expand=Sagskategori,Sagsstatus,Sagstype&%24top=2"
# Cases with steps and documents
curl "https://oda.ft.dk/api/Sag?%24expand=Sagstrin,SagDokument&%24top=2"
# Votes with session, politician, and vote type
curl "https://oda.ft.dk/api/Stemme?%24expand=Afstemning,Aktør,Stemmetype&%24top=5"
Performance Considerations for Multiple Expansions¶
| Expansion Count | Response Time | Recommendation |
|---|---|---|
| 1-2 expansions | ~90-150ms | ✅ Optimal |
| 3-4 expansions | ~200-400ms | Monitor performance |
| 5+ expansions | ~500ms+ | L Consider multiple requests |
Nested Relationship Expansion¶
Navigate through multiple levels of relationships:
Two-Level Expansions (Most Common)¶
# Votes with politician biography information
curl "https://oda.ft.dk/api/Stemme?%24expand=Aktør&%24top=5"
# Note: Aktør includes biografi field directly
# Cases with actor relationships and actor details
curl "https://oda.ft.dk/api/Sag?%24expand=SagAktør/Aktør&%24top=2"
# Cases with document relationships and document details
curl "https://oda.ft.dk/api/Sag?%24expand=SagDokument/Dokument&%24top=2"
# Voting sessions with individual votes and politicians
curl "https://oda.ft.dk/api/Afstemning?%24expand=Stemme/Aktør&%24top=1"
Three-Level Expansions (Maximum Depth)¶
# Documents with actor relationships and full actor details
curl "https://oda.ft.dk/api/Dokument?%24expand=DokumentAktør/Aktør/Aktørtype&%24top=1"
# Cases with steps and step documents
curl "https://oda.ft.dk/api/Sag?%24expand=Sagstrin/SagstrinDokument/Dokument&%24top=1"
Performance Warning
Three-level expansions can be very slow (~2+ seconds). Use sparingly and with small $top values.
Junction Table Expansion Patterns¶
The Danish Parliament API uses junction tables extensively to model complex relationships. Master these patterns for effective data access.
SagAktør (Case-Actor Junction)¶
The SagAktør entity connects cases to actors with 23 different role types:
# Cases with all related actors and their roles
curl "https://oda.ft.dk/api/Sag?%24expand=SagAktør&%24top=2"
# Cases with actor details through junction table
curl "https://oda.ft.dk/api/Sag?%24expand=SagAktør/Aktør&%24top=2"
# Cases with actor details AND role information
curl "https://oda.ft.dk/api/Sag?%24expand=SagAktør/Aktør,SagAktør/SagAktørRolle&%24top=2"
# Direct junction table access with full expansion
curl "https://oda.ft.dk/api/SagAktør?%24expand=Sag,Aktør,SagAktørRolle&%24top=10"
DokumentAktør (Document-Actor Junction)¶
The DokumentAktør entity connects documents to actors with 25 role types:
# Documents with all related actors
curl "https://oda.ft.dk/api/Dokument?%24expand=DokumentAktør&%24top=3"
# Documents with actor details through junction
curl "https://oda.ft.dk/api/Dokument?%24expand=DokumentAktør/Aktør&%24top=3"
# Direct access to document-actor relationships
curl "https://oda.ft.dk/api/DokumentAktør?%24expand=Dokument,Aktør,DokumentAktørRolle&%24top=10"
SagDokument (Case-Document Junction)¶
# Cases with all related documents
curl "https://oda.ft.dk/api/Sag?%24expand=SagDokument&%24top=2"
# Cases with full document details
curl "https://oda.ft.dk/api/Sag?%24expand=SagDokument/Dokument&%24top=2"
# Documents attached to cases with relationship info
curl "https://oda.ft.dk/api/SagDokument?%24expand=Sag,Dokument,SagDokumentRolle&%24top=10"
Real-World Expansion Examples¶
Political Analysis Expansions¶
Get Complete Politician Voting Records¶
# All votes by Frank Aaen with session details
curl "https://oda.ft.dk/api/Stemme?%24filter=Aktør/navn%20eq%20'Frank%20Aaen'&%24expand=Afstemning,Aktør&%24top=20"
Get Legislative Process Details¶
# Climate legislation with complete process information
curl "https://oda.ft.dk/api/Sag?%24filter=substringof('klima',titel)&%24expand=Sagstrin,SagAktør/Aktør&%24top=3"
Get Committee Work Overview¶
# Cases handled by specific committees with actor details
curl "https://oda.ft.dk/api/SagAktør?%24filter=substringof('udvalg',Aktør/navn)&%24expand=Sag,Aktør&%24top=10"
Document Analysis Expansions¶
Get Complete Document Context¶
# Documents with authors and attached files
curl "https://oda.ft.dk/api/Dokument?%24expand=DokumentAktør/Aktør,Fil&%24top=5"
Get Parliamentary Meeting Records¶
# Meetings with agenda items and related documents
curl "https://oda.ft.dk/api/Møde?%24expand=Dagsordenspunkt&%24top=3"
Voting Analysis Expansions¶
Complete Voting Session Analysis¶
# Voting sessions with all individual votes and politicians
curl "https://oda.ft.dk/api/Afstemning?%24expand=Stemme/Aktør&%24top=1"
Performance Impact
This query can take 2+ seconds due to the large number of individual votes per session.
Efficient Voting Analysis Alternative¶
# Get voting session details first
curl "https://oda.ft.dk/api/Afstemning?%24top=1&%24expand=Møde"
# Then get votes for specific session
curl "https://oda.ft.dk/api/Stemme?%24filter=afstemningid%20eq%2012345&%24expand=Aktør&%24top=100"
Error Handling in Expansions¶
Valid vs. Invalid Expansion Names¶
Valid Expansions (HTTP 200)¶
# These work correctly
curl "https://oda.ft.dk/api/Sag?%24expand=Sagskategori&%24top=1" # ✅
curl "https://oda.ft.dk/api/Sag?%24expand=SagAktør/Aktør&%24top=1" # ✅
curl "https://oda.ft.dk/api/Stemme?%24expand=Afstemning,Aktør&%24top=1" # ✅
Invalid Expansions (HTTP 400)¶
# These return HTTP 400 Bad Request
curl -i "https://oda.ft.dk/api/Sag?%24expand=NonExistentRelation&%24top=1" # L
curl -i "https://oda.ft.dk/api/Sag?%24expand=InvalidPath/BadEntity&%24top=1" # L
Error Response Format¶
{
"odata.error": {
"code": "",
"message": {
"lang": "en-US",
"value": "Could not find a property named 'NonExistentRelation'..."
}
}
}
Performance Optimization Strategies¶
1. Strategic Expansion Selection¶
# Efficient: Only expand what you need
curl "https://oda.ft.dk/api/Sag?%24expand=Sagskategori&%24top=10"
# Less efficient: Expanding everything
curl "https://oda.ft.dk/api/Sag?%24expand=Sagskategori,Sagsstatus,Sagstype,SagAktør,SagDokument&%24top=10"
2. Filter Before Expansion¶
# Efficient: Filter first, then expand
curl "https://oda.ft.dk/api/Sag?%24filter=year(opdateringsdato)%20eq%202025&%24expand=SagAktør/Aktør&%24top=10"
# Less efficient: Expand first, filter gets applied to larger dataset
curl "https://oda.ft.dk/api/Sag?%24expand=SagAktør/Aktør&%24filter=year(opdateringsdato)%20eq%202025&%24top=10"
3. Pagination with Expansion¶
# Efficient pattern for large expanded datasets
for skip in {0..500..50}; do # Note: smaller batch size with expansion
curl "https://oda.ft.dk/api/Sag?%24expand=SagAktør/Aktør&%24skip=$skip&%24top=50"
sleep 0.2 # Rate limiting for complex queries
done
4. Alternative Multi-Request Strategy¶
Sometimes multiple simple requests are faster than complex expansions:
# Python: Multi-request strategy for complex data
import requests
# Strategy 1: Single complex request (may be slow)
complex_url = "https://oda.ft.dk/api/Sag?$expand=SagAktør/Aktør,SagDokument/Dokument&$top=10"
# Strategy 2: Multiple simple requests (may be faster)
cases = requests.get("https://oda.ft.dk/api/Sag?$top=10").json()
for case in cases['value']:
case_id = case['id']
# Get actors for this case
actors_url = f"https://oda.ft.dk/api/SagAktør?$filter=sagid eq {case_id}&$expand=Aktør"
case['actors'] = requests.get(actors_url).json()['value']
# Get documents for this case
docs_url = f"https://oda.ft.dk/api/SagDokument?$filter=sagid eq {case_id}&$expand=Dokument"
case['documents'] = requests.get(docs_url).json()['value']
Complete Entity Relationship Map¶
Core Entities and Their Expansions¶
Sag (Cases) - 15+ Expandable Relationships¶
Sagskategori- Case categorySagsstatus- Case statusSagstype- Case typePeriode- Parliamentary periodSagstrin- Case stepsSagAktør- Related actors (junction)SagDokument- Related documents (junction)EmneordSag- Keywords (junction)DagsordenspunktSag- Agenda items (junction)
Aktør (Actors) - 10+ Expandable Relationships¶
Aktørtype- Actor typeAktørAktør- Actor relationships (junction)SagAktør- Related cases (junction)DokumentAktør- Related documents (junction)MødeAktør- Meeting participation (junction)SagstrinAktør- Case step participation (junction)
Dokument (Documents) - 8+ Expandable Relationships¶
Dokumenttype- Document typeDokumentstatus- Document statusDokumentkategori- Document categoryFil- Attached filesDokumentAktør- Related actors (junction)SagDokument- Related cases (junction)EmneordDokument- Keywords (junction)
Advanced Expansion Patterns¶
Conditional Expansion Based on Entity Type¶
# Python: Smart expansion based on entity characteristics
def expand_entity_smartly(entity_name, record_id):
"""Apply optimal expansion strategy based on entity type"""
expansion_strategies = {
'Sag': 'Sagskategori,Sagsstatus', # Core info only
'Aktør': 'Aktørtype', # Type information
'Stemme': 'Afstemning,Aktør', # Full voting context
'Dokument': 'Dokumenttype,Fil', # Type and files
'Afstemning': 'Møde', # Meeting context (not all votes - too slow)
}
expand = expansion_strategies.get(entity_name, '')
url = f"https://oda.ft.dk/api/{entity_name}?$filter=id eq {record_id}"
if expand:
url += f"&$expand={expand}"
return requests.get(url).json()
Expansion-Aware Pagination¶
# Python: Adjust pagination batch size based on expansion complexity
def get_expansion_batch_size(expansion_string):
"""Recommend batch size based on expansion complexity"""
if not expansion_string:
return 100 # No expansion: use maximum
expansion_count = expansion_string.count(',') + 1
nested_levels = expansion_string.count('/')
# Reduce batch size for complex expansions
if nested_levels >= 2:
return 10 # Very complex: small batches
elif expansion_count >= 3:
return 25 # Multiple expansions: medium batches
else:
return 50 # Simple expansion: standard batches
# Usage
expansion = "SagAktør/Aktør,SagDokument/Dokument"
batch_size = get_expansion_batch_size(expansion) # Returns 10
url = f"https://oda.ft.dk/api/Sag?$expand={expansion}&$top={batch_size}"
Best Practices Summary¶
- Use URL encoding (
%24expandnot$expand) - Start with simple expansions and add complexity gradually
- Filter before expanding to reduce data processing
- Monitor response times and adjust batch sizes accordingly
- Use junction tables strategically to access relationship data
- Consider multi-request strategies for very complex data needs
- Cache frequently accessed relationship data to minimize API calls
- Test expansion paths against metadata to avoid HTTP 400 errors
Quick Reference¶
Most Useful Expansion Patterns¶
# Cases with essential metadata
?%24expand=Sagskategori,Sagsstatus
# Complete voting context
?%24expand=Afstemning,Aktør,Stemmetype
# Document with files and author
?%24expand=Fil,DokumentAktør/Aktør
# Junction table with full relationship context
?%24expand=Sag,Aktør,SagAktørRolle
Performance Guidelines¶
- 1-2 expansions: ~90-150ms (✅ Optimal)
- 3+ expansions: ~200ms+ ( Monitor)
- Nested expansions: ~500ms+ (L Use carefully)
- Voting with actors: ~2s+ (L Paginate aggressively)
The Danish Parliament API's expansion capabilities provide powerful access to the complex relationships within parliamentary data. Master these patterns and you'll efficiently navigate the interconnected web of Danish democratic information.