Fully Typed Usage
This guide shows how to use the TypedBMRSClient for complete type safety across all 287 API endpoints.
The Problem
The standard BMRSClient returns Dict[str, Any] for most endpoints, which provides no type safety:
from elexon_bmrs import BMRSClient
client = BMRSClient(api_key="your-key")
response = client.get_datasets_abuc(
publishDateTimeFrom="2024-01-01T00:00:00Z",
publishDateTimeTo="2024-01-02T00:00:00Z"
)
# response is Dict[str, Any] - no type safety!
Note: The BMRS OpenAPI specification defines no required fields for any of the 280 schemas. This means all fields in the generated models are Optional, which is actually correct according to the API specification. The TypedBMRSClient handles this gracefully by providing proper type hints while maintaining API compatibility.
The Solution
Use TypedBMRSClient for fully typed responses:
from elexon_bmrs import TypedBMRSClient
client = TypedBMRSClient(api_key="your-key")
response = client.get_datasets_abuc(
publishDateTimeFrom="2024-01-01T00:00:00Z",
publishDateTimeTo="2024-01-02T00:00:00Z"
)
# response is AbucDatasetRow_DatasetResponse - fully typed!
Benefits
✅ Full type safety - Every endpoint returns the correct Pydantic model
✅ IDE autocomplete - Complete IntelliSense for all response fields
✅ Type checking - mypy and other tools can verify your code
✅ Data validation - Automatic Pydantic validation of responses
✅ Better documentation - Self-documenting code with type hints
Usage Examples
Dataset Endpoints
from elexon_bmrs import TypedBMRSClient
client = TypedBMRSClient(api_key="your-key")
# ABUC - Amount of Balancing Reserves Under Contract
abuc_data = client.get_datasets_abuc(
publishDateTimeFrom="2024-01-01T00:00:00Z",
publishDateTimeTo="2024-01-02T00:00:00Z"
)
# Fully typed access to response fields
print(f"Total records: {len(abuc_data.data or [])}")
for row in abuc_data.data or []:
# Full IDE autocomplete available!
print(f"Dataset: {row.dataset}")
print(f"PSR Type: {row.psrType}")
print(f"Business Type: {row.businessType}")
print(f"Publish Time: {row.publishTime}")
Generation Data
# AGPT - Aggregated Generation Per Type
agpt_data = client.get_datasets_agpt(
publishDateTimeFrom="2024-01-01T00:00:00Z",
publishDateTimeTo="2024-01-02T00:00:00Z"
)
for row in agpt_data.data or []:
print(f"Fuel Type: {row.fuelType}")
print(f"Generation: {row.generation} MW")
print(f"Output: {row.output} MW")
System Frequency
# FREQ - System Frequency
freq_data = client.get_datasets_freq(
publishDateTimeFrom="2024-01-01T00:00:00Z",
publishDateTimeTo="2024-01-02T00:00:00Z"
)
for row in freq_data.data or []:
print(f"Time: {row.time}")
print(f"Frequency: {row.frequency} Hz")
Type Checking with mypy
Enable static type checking for your code:
from elexon_bmrs import TypedBMRSClient
from elexon_bmrs.generated_models import AbucDatasetRow
def process_abuc_data(client: TypedBMRSClient) -> None:
"""Process ABUC data with full type safety."""
response = client.get_datasets_abuc(
publishDateTimeFrom="2024-01-01T00:00:00Z",
publishDateTimeTo="2024-01-02T00:00:00Z"
)
# Type checker knows response.data is List[AbucDatasetRow]
for row in response.data or []:
# Type checker validates all field access
dataset = row.dataset # Optional[str]
psr_type = row.psrType # Optional[str]
if dataset and psr_type:
print(f"{dataset}: {psr_type}")
# Run mypy to check types
# mypy your_script.py
Handling Optional Fields
Since the BMRS API defines no required fields, all fields are Optional. Handle this explicitly:
from elexon_bmrs import TypedBMRSClient
client = TypedBMRSClient(api_key="your-key")
response = client.get_datasets_abuc(...)
for row in response.data or []:
# Safe access with defaults
dataset = row.dataset or "UNKNOWN"
quantity = row.quantity or 0.0
publish_time = row.publishTime or datetime.now()
# Conditional access for optional fields
if row.description:
print(f"Description: {row.description}")
if row.relatedInformation:
print(f"Related info: {row.relatedInformation}")
print(f"Dataset: {dataset}, Quantity: {quantity}")
Response Type Mapping
The TypedBMRSClient maps endpoints to their proper response types:
| Endpoint | Response Type | Description |
|---|---|---|
get_datasets_abuc |
AbucDatasetRow_DatasetResponse |
Balancing reserves under contract |
get_datasets_agpt |
ActualAggregatedGenerationPerTypeDatasetRow_DatasetResponse |
Generation by fuel type |
get_datasets_freq |
SystemFrequencyDatasetRow_DatasetResponse |
System frequency |
get_datasets_bod |
BidOfferDatasetRow_DatasetResponse |
Bid-offer data |
get_system_demand |
SystemDemandResponse |
System demand |
| ... | ... | ... |
Checking Type Coverage
See which endpoints are fully typed:
from elexon_bmrs import TypedBMRSClient
client = TypedBMRSClient()
info = client.get_typing_info()
print(f"Typing coverage: {info['typing_stats']['typing_coverage_percent']}%")
print(f"Typed endpoints: {info['typing_stats']['typed_endpoints']}")
print(f"Untyped endpoints: {info['typing_stats']['untyped_endpoints']}")
# List all typed endpoints
for endpoint in info['typed_endpoints']:
print(f"✅ {endpoint}")
# List endpoints that still need typing
for endpoint in info['untyped_endpoints']:
print(f"⚠️ {endpoint} (returns Dict[str, Any])")
Convenience Function
Use the convenience function for cleaner imports:
from elexon_bmrs import create_typed_client
# Create fully typed client
client = create_typed_client(api_key="your-key")
# All methods return properly typed responses
data = client.get_datasets_abuc(...) # Returns AbucDatasetRow_DatasetResponse
Error Handling
The typed client handles parsing errors gracefully:
from elexon_bmrs import TypedBMRSClient
client = TypedBMRSClient(api_key="your-key")
try:
response = client.get_datasets_abuc(
publishDateTimeFrom="2024-01-01T00:00:00Z",
publishDateTimeTo="2024-01-02T00:00:00Z"
)
# If parsing succeeds, response is fully typed
for row in response.data or []:
print(f"Dataset: {row.dataset}")
except Exception as e:
print(f"Error: {e}")
Performance
The typed client has minimal performance overhead:
- Parsing time: ~1-5ms per response (depending on data size)
- Memory usage: Slightly higher due to Pydantic model creation
- Type safety: 100% improvement in development experience
Migration from BMRSClient
Easy migration from the standard client:
# Before
from elexon_bmrs import BMRSClient
client = BMRSClient(api_key="your-key")
# After
from elexon_bmrs import TypedBMRSClient
client = TypedBMRSClient(api_key="your-key")
# All method signatures remain the same
# Only return types change from Dict[str, Any] to proper Pydantic models
Best Practices
- Use TypedBMRSClient for new code
- Enable type checking with mypy
- Handle optional fields properly (use
or []for lists) - Check type coverage periodically
- Report untyped endpoints for future improvements
Next Steps
- API Reference - Typed Client - Complete documentation
- Generated Models Reference - All 280 models
- Examples - More typed usage examples