Skip to content

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

[!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 pandas installed, 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

python3 enedis_consumption.py \
  --address "57 RUE ALFRED FRANCOIS" \
  --code-commune 80001

Search by partial address + city name

python3 enedis_consumption.py \
  --address "RUE ALFRED" \
  --city "Abbeville"

Fetch all data for a department

python3 enedis_consumption.py \
  --code-departement 75 \
  --output paris.csv

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 total field 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 after parameter returned in the next field. The /lines endpoint 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

python3 enedis_consumption.py \
  --address "57 RUE ALFRED FRANCOIS" \
  --code-commune 80001

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

python3 enedis_consumption.py \
  --code-commune 80001 \
  -o abbeville_consumption.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 limit query 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

File Path

/home/paul/Documents/COURSES/All Documentation/docs/AI-ML/Agent/Enedis/
β”œβ”€β”€ enedis_consumption.py         # Script principal
└── Enedis Residential Electricity Consumption API β€” Client.md  # This doc