Python wrapper for the Fluvius Energy API v3 (ESCO - Energy Service Company API).
pip install fluvius-energy-apiOr with uv:
uv add fluvius-energy-apiThe client requires OAuth2 credentials for Azure AD authentication. Two authentication methods are supported:
- Certificate-based (production): Uses certificate thumbprint + private key
- Client secret (sandbox): Uses a simple client secret
These are shared between production and sandbox:
| Variable | Description |
|---|---|
FLUVIUS_SUBSCRIPTION_KEY |
Azure API Management subscription key |
FLUVIUS_CLIENT_ID |
Azure AD application (client) ID |
FLUVIUS_TENANT_ID |
Azure AD tenant ID |
FLUVIUS_SCOPE |
OAuth2 scope (e.g., api://your-app-id/.default) |
| Variable | Description |
|---|---|
FLUVIUS_CERTIFICATE_THUMBPRINT |
Certificate thumbprint (hex format) |
FLUVIUS_PRIVATE_KEY |
RSA private key in PEM format |
FLUVIUS_PRIVATE_KEY_PATH |
Alternative: path to private key file |
| Variable | Description |
|---|---|
FLUVIUS_SANDBOX_CLIENT_SECRET |
Client secret for sandbox |
Note: For sandbox, common values (subscription_key, client_id, tenant_id, scope, data_access_contract_number) automatically fall back to FLUVIUS_* if not set with FLUVIUS_SANDBOX_* prefix.
| Variable | Description |
|---|---|
FLUVIUS_DATA_ACCESS_CONTRACT_NUMBER |
Data access contract number (required, typically constant per service provider) |
The private key can be provided in three ways:
- Inline PEM: Set
FLUVIUS_PRIVATE_KEYwith the full PEM content - Base64-encoded: Set
FLUVIUS_PRIVATE_KEYwith the PEM content encoded as base64 - File path: Set
FLUVIUS_PRIVATE_KEY_PATHto point to a.pemfile
from fluvius_energy_api import FluviusEnergyClient, Environment
# Credentials loaded from FLUVIUS_* environment variables
with FluviusEnergyClient(environment=Environment.PRODUCTION) as client:
response = client.get_mandates()
for mandate in response.data.mandates:
print(f"EAN: {mandate.ean}, Status: {mandate.status}")from fluvius_energy_api import FluviusEnergyClient, FluviusCredentials, Environment
# Load credentials - falls back to FLUVIUS_* for common values,
# uses FLUVIUS_SANDBOX_CLIENT_SECRET for auth
sandbox_credentials = FluviusCredentials.from_env(prefix="FLUVIUS_SANDBOX")
with FluviusEnergyClient(credentials=sandbox_credentials, environment=Environment.SANDBOX) as client:
response = client.get_mandates()
print(f"Found {len(response.data.mandates)} mandates in sandbox")from fluvius_energy_api import FluviusEnergyClient, FluviusCredentials, Environment
# Production with certificate
prod_credentials = FluviusCredentials(
subscription_key="your-subscription-key",
client_id="your-client-id",
tenant_id="your-tenant-id",
scope="api://your-scope/.default",
certificate_thumbprint="YOUR_CERTIFICATE_THUMBPRINT",
private_key="-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----",
)
# Sandbox with client secret
sandbox_credentials = FluviusCredentials(
subscription_key="your-subscription-key",
client_id="your-client-id",
tenant_id="your-tenant-id",
scope="api://your-scope/.default",
client_secret="your-client-secret",
)
with FluviusEnergyClient(credentials=prod_credentials, environment=Environment.PRODUCTION) as client:
mandates = client.get_mandates()Create a session for end-users to grant data access:
from fluvius_energy_api import (
FluviusEnergyClient,
ClientSessionDataService,
DataServiceType,
Environment,
)
with FluviusEnergyClient(environment=Environment.PRODUCTION) as client:
response = client.create_client_session(
data_access_contract_number="3599900040",
reference_number="my-unique-reference",
flow="B2B",
data_services=[
ClientSessionDataService(
data_service_type=DataServiceType.VH_DAG,
data_period_from="2024-01-01T00:00:00Z",
),
ClientSessionDataService(
data_service_type=DataServiceType.VH_KWARTIER_UUR,
data_period_from="2024-01-01T00:00:00Z",
),
],
number_of_eans=5,
)
if response.data.status == "success":
# Build the consent URL for end-users
consent_url = f"https://mijn.fluvius.be/verbruik/dienstverlener?id={response.data.short_url_identifier}"
print(f"Consent URL: {consent_url}")
print(f"Valid until: {response.data.valid_to}")Retrieve mandates with optional filters:
from fluvius_energy_api import (
FluviusEnergyClient,
EnergyType,
MandateStatus,
DataServiceType,
Environment,
)
with FluviusEnergyClient(environment=Environment.PRODUCTION) as client:
# Get all mandates
response = client.get_mandates()
# Get mandates with filters
response = client.get_mandates(
ean="541448800000004312",
energy_type=EnergyType.ELECTRICITY,
status=MandateStatus.APPROVED,
data_service_types=[DataServiceType.VH_DAG, DataServiceType.VH_KWARTIER_UUR],
)
for mandate in response.data.mandates:
print(f"EAN: {mandate.ean}")
print(f"Status: {mandate.status}")
print(f"Energy type: {mandate.energy_type}")
print(f"Data service: {mandate.data_service_type}")Retrieve energy consumption/production data:
from datetime import datetime
from fluvius_energy_api import FluviusEnergyClient, PeriodType, Environment
with FluviusEnergyClient(environment=Environment.PRODUCTION) as client:
response = client.get_energy(
ean="541448800000004312",
period_type=PeriodType.READ_TIME,
granularity="daily",
from_date=datetime(2024, 1, 1),
to_date=datetime(2024, 6, 1),
)
headpoint = response.data.headpoint
print(f"EAN: {headpoint.ean}")
print(f"Energy type: {headpoint.energy_type}")A hand-crafted Postman collection is included in the postman/ directory covering authentication, client sessions, mandates, and energy data endpoints.
To generate the environment files from your .env:
python postman/generate_postman_env.pyThis creates two files (gitignored):
postman/fluvius-sandbox.postman_environment.json-- client-secret authpostman/fluvius-production.postman_environment.json-- certificate / JWT auth
Import the collection (postman/fluvius-energy-api.postman_collection.json) and the appropriate environment into Postman. The collection's pre-request script acquires tokens automatically.
The production client assertion is valid for 1 hour. Re-run the script for a fresh one -- the JWT is printed to stdout so you can copy-paste it directly into your already-imported Postman environment without re-importing the file.
The client supports two environments:
| Environment | Base URL | Auth Method |
|---|---|---|
Environment.PRODUCTION |
https://apihub.fluvius.be/esco-live/v3 |
Certificate |
Environment.SANDBOX |
https://apihub.fluvius.be/esco-sbx/v3 |
Client secret |
from fluvius_energy_api import FluviusEnergyClient, Environment
# Production
client = FluviusEnergyClient(environment=Environment.PRODUCTION)
# Sandbox
client = FluviusEnergyClient(environment=Environment.SANDBOX)The client raises specific exceptions for different error scenarios:
from fluvius_energy_api import (
FluviusEnergyClient,
FluviusAPIError,
AuthenticationError,
ForbiddenError,
NotFoundError,
ValidationError,
ServerError,
ServiceUnavailableError,
ConfigurationError,
Environment,
)
try:
with FluviusEnergyClient(environment=Environment.PRODUCTION) as client:
response = client.get_mandates()
except ConfigurationError as e:
print(f"Configuration error: {e}")
except AuthenticationError as e:
print(f"Authentication failed: {e}")
except ForbiddenError as e:
print(f"Access forbidden: {e}")
except NotFoundError as e:
print(f"Resource not found: {e}")
except ValidationError as e:
print(f"Validation error: {e}")
if e.validation_errors:
for error in e.validation_errors:
print(f" - {error}")
except ServerError as e:
print(f"Server error: {e}")
except ServiceUnavailableError as e:
print(f"Service unavailable: {e}")
except FluviusAPIError as e:
print(f"API error: {e}")Available data service types:
| Type | Description |
|---|---|
VH_DAG |
Daily consumption history |
VH_KWARTIER_UUR |
Quarter-hourly/hourly consumption history |
VH_ONBEPAALD |
Indefinite consumption history |
IG |
Installation data |
GNU AFFERO GENERAL PUBLIC LICENSE