Enedis Residential Electricity Consumption API β Client
Overview
This Python client retrieves annual electricity consumption data for French residential addresses from the Enedis Open Data API. The dataset, "Consommation d'Γ©lectricitΓ© annuelle rΓ©sidentielle par adresse", provides the total annual consumption in MWh for every residential address in France β grouped by year, commune, and address.
The data is published by Enedis (the French electricity grid operator) under an open license and contains approximately 2.8 million records spanning from 2019 to 2024. This client handles cursor-based pagination automatically, supports fuzzy or exact address search, and can filter by commune, department, or year range.
Source
- Dataset: Consommation annuelle rΓ©sidentielle par adresse
- API docs: Swagger via data-fair API
[!NOTE] The API is rate-limited: 600 requests / 60s for anonymous users, 1 200 for authenticated users. The client stays well under these limits but fetches all pages sequentially.
Installation
The script uses only Python stdlib β no external dependencies required.
# Save the script anywhere, e.g.:
wget -O enedis_consumption.py https://your-source/enedis_consumption.py
chmod +x enedis_consumption.py
[!TIP] If you have
pandasinstalled, you can pipe the JSON output directly into a DataFrame:import pandas as pd; df = pd.DataFrame(results)
Quick Start
Search by address and commune
Search by partial address + city name
Fetch all data for a department
Filter by specific years
python3 enedis_consumption.py \
--address "RUE DE LA PAIX" \
--code-commune 75101 \
--years 2022 2023 2024
Verbose mode + JSON export
python3 enedis_consumption.py \
--address "57 RUE ALFRED FRANCOIS" \
--code-commune 80001 \
-v \
--json results.json
API Reference
Function: get_consumption_by_address()
get_consumption_by_address(
address: str | None = None,
code_commune: str | None = None,
nom_commune: str | None = None,
code_departement: str | None = None,
years: list[str] | None = None,
exact_match: bool = False,
as_dataframe: bool = True,
) -> pd.DataFrame | list[dict]
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
address |
str or None |
None |
Full or partial address text. Fuzzy search by default. |
code_commune |
str or None |
None |
Exact INSEE commune code (e.g. "80001" for Abbeville). |
nom_commune |
str or None |
None |
Commune name β exact match. |
code_departement |
str or None |
None |
Department code (2 or 3 digits, e.g. "75", "2A"). |
years |
list[str] or None |
["2021", "2022", "2023", "2024"] |
Years to include. |
exact_match |
bool |
False |
When True, address is matched exactly instead of fuzzy search. |
as_dataframe |
bool |
True |
Return a pd.DataFrame (requires pandas). Falls back to list[dict] if pandas is unavailable. |
Return Value β pd.DataFrame (default) or list[dict]
When as_dataframe=True and pandas is installed, numeric columns are cast to proper types:
| Column | Type | Description |
|---|---|---|
annee |
str |
Year (e.g. "2024") |
adresse |
str |
Full address string |
code_commune |
str |
INSEE commune code |
nom_commune |
str |
Commune name |
consommation_annuelle_totale_de_ladresse_mwh |
float64 |
Total annual consumption (MWh) |
consommation_annuelle_moyenne_par_site_de_ladresse_mwh |
float64 |
Average per site (MWh) |
nombre_de_logements |
Int64 |
Number of dwellings (nullable integer) |
code_departement |
str |
Department code |
code_region |
str |
Region code |
segment_de_client |
str |
Customer segment |
CLI Reference
Usage
usage: enedis_consumption.py [-h] [--address ADDRESS] [--exact]
[--code-commune CODE_COMMUNE] [--city NOM_COMMUNE]
[--code-departement CODE_DEPARTEMENT]
[--years YEARS [YEARS ...]]
[--output OUTPUT] [--json JSON] [--verbose]
Arguments
| Argument | Description |
|---|---|
--address ADDRESS |
Address text to search (partial or full). Fuzzy match by default. |
--exact |
Match address exactly instead of fuzzy search. |
--code-commune CODE |
INSEE commune code (e.g. 80001). |
--city NAME, --nom-commune NAME |
Commune name β exact match. |
--code-departement CODE |
Department code (2 or 3 digits). |
--years Y1 [Y2 ...] |
Years to include. Default: 2021 2022 2023 2024. |
--output FILE, -o FILE |
Save results as CSV. |
--json FILE, -j FILE |
Save results as JSON. |
--verbose, -v |
Print record count and timing info. |
[!WARNING] At least one filter is required:
--address,--code-commune,--city, or--code-departement. Running with no filters will error.
Data Schema
The API returns the following columns from the /lines endpoint:
| Column | Type | Description |
|---|---|---|
annee |
string |
Year |
code_iris |
string |
IRIS code (neighbourhood level) |
nom_iris |
string |
IRIS name |
numero_de_voie |
string |
Street number |
indice_de_repetition |
string |
Repetition index (bis, ter, etc.) |
type_de_voie |
string |
Street type (RUE, AV, BD, etc.) |
libelle_de_voie |
string |
Street name |
adresse |
string |
Full address string |
code_commune |
string |
INSEE commune code |
nom_commune |
string |
Commune name |
segment_de_client |
string |
Customer segment |
nombre_de_logements |
integer |
Number of dwellings at the address |
consommation_annuelle_totale_de_ladresse_mwh |
number |
Total annual consumption (MWh) |
consommation_annuelle_moyenne_par_site_de_ladresse_mwh |
number |
Average consumption per site (MWh) |
consommation_annuelle_moyenne_de_la_commune_mwh |
number |
Average consumption of the commune (MWh) |
code_epci |
string |
EPCI code (intercommunal structure) |
code_departement |
string |
Department code |
code_region |
string |
Region code |
[!NOTE] The dataset contains approximately 2.8 million records (updated annually). The
totalfield in API responses reflects the total matching count, not the page size.
Rate Limiting
| User type | Requests / 60s | Download speed |
|---|---|---|
| Anonymous | 600 | 8 MB/s (static), 500 kB/s (API) |
| Authenticated | 1 200 | 16 MB/s (static), 1 MB/s (API) |
[!TIP] The client follows cursor-based pagination via the
afterparameter returned in thenextfield. The/linesendpoint returns 12 results per page by default. A commune-wide search typically fetches 100β500 records and completes in 5β15 seconds.
Usage Examples
Example 1: Single address, all years
Output:
Year Address Commune Dept Dwellings Total (MWh) Avg/Site (MWh)
2021 57 RUE ALFRED FRANCOIS Abbeville 80 11 1.436 0.131
2022 57 RUE ALFRED FRANCOIS Abbeville 80 12 13.057 1.088
2023 57 RUE ALFRED FRANCOIS Abbeville 80 12 11.371 0.948
2024 57 RUE ALFRED FRANCOIS Abbeville 80 10 9.836 0.984
Example 2: Export whole commune to CSV
Example 3: Using in Python code
import json, subprocess
def get_consumption(address: str, commune: str) -> list[dict]:
result = subprocess.run(
["python3", "enedis_consumption.py",
"--address", address,
"--code-commune", commune,
"--json", "/tmp/result.json"],
capture_output=True, text=True
)
with open("/tmp/result.json") as f:
return json.load(f)
data = get_consumption("57 RUE ALFRED FRANCOIS", "80001")
for row in data:
print(f"{row['annee']}: {row['consommation_annuelle_totale_de_ladresse_mwh']} MWh")
Example 4: Direct Python import with DataFrame
import sys
sys.path.insert(0, "/path/to/script")
from enedis_consumption import get_consumption_by_address
# Returns a DataFrame by default (requires pandas)
df = get_consumption_by_address(
address="RUE ALFRED",
code_commune="80001",
years=["2022", "2023", "2024"]
)
# Explore and plot
print(df.shape) # (N rows, 11 columns)
print(df.dtypes) # float64, Int64, str
print(df.describe())
# Total consumption
total = df["consommation_annuelle_totale_de_ladresse_mwh"].sum()
print(f"Total: {total:.3f} MWh")
# Plot yearly trend
df.groupby("annee")["consommation_annuelle_totale_de_ladresse_mwh"] \
.sum() \
.plot(kind="bar", title="Annual Consumption (MWh)")
# Scatter: dwellings vs consumption
df.plot.scatter(
x="nombre_de_logements",
y="consommation_annuelle_totale_de_ladresse_mwh",
title="Dwellings vs Consumption"
)
Pagination Details
The Enedis API uses cursor-based pagination rather than standard offset/limit. Each response includes a next field containing the full URL for the next page, with an opaque after= parameter.
{
"total": 1218381,
"next": "https://.../lines?select=...&annee_in=2022,2023,2024&after=4,809287",
"results": [...]
}
The client function _fetch_all_lines() follows these links automatically until next is absent.
[!TIP] The
limitquery parameter is not supported by this API. The response always returns 12 results per page.
Error Handling
| Scenario | Behaviour |
|---|---|
| No results matching filters | Returns empty list, prints warning to stderr |
| Network timeout | urllib.error.URLError raised (30s timeout) |
| Missing filter arguments | CLI exits with error message listing required params |
| Rate limit (HTTP 429) | urllib.error.HTTPError raised β wait before retrying |