Async Python client for the Veolia water portal API (eau.veolia.fr).
Table of contents
Installation
First of all, you need to install devbox if you don't have a python environment
Once the previous step is done, simply run
devbox shell
cp .env.example .env # fill in your credentials
python usage_example.py
That's it !
If you already have a python environment just run
pip install veolia-api-foxace
Usage
"""Example of usage of the Veolia API"""
import asyncio
from datetime import date
import aiohttp
from veolia_api.veolia_api import VeoliaAPI
async def main() -> None:
"""Main function."""
async with aiohttp.ClientSession() as session:
client_api = VeoliaAPI("your@email.com", "password", session)
await client_api.fetch_all_data(date(2025, 1, 1), date(2025, 9, 1))
# Display fetched data
print(client_api.account_data.daily_consumption)
print(client_api.account_data.monthly_consumption)
print(client_api.account_data.alert_settings.daily_enabled)
if __name__ == "__main__":
asyncio.run(main())
You can use usage_example.py
Portals
Veolia operates several portals. They share the same Cognito authentication flow
but each has its own client_id, and some run on a dedicated data backend. Select
a portal with the portal_url argument (defaults to the national portal):
client_api = VeoliaAPI("your@email.com", "password", session, portal_url="www.ea-pm.fr")
portal_url |
Description | Backend |
|---|---|---|
eau.veolia.fr (default) |
Veolia France (national) | default |
eaudetm.monespace.eau.veolia.fr |
Eau de Toulouse Métropole | default |
www.ea-pm.fr |
Eau de Perpignan Méditerranée Métropole | dedicated |
You can resolve a commune name to its portal at setup time:
from veolia_api import resolve_portal_url
portal = await resolve_portal_url("Toulouse") # "eaudetm.monespace.eau.veolia.fr"
client_api = VeoliaAPI("your@email.com", "password", session, portal_url=portal)
To add a portal, add an entry to VEOLIA_PORTALS in
veolia_api/portals.py with its client_id (found in
the portal's JavaScript bundle as ClientId:"...") and, if different from the
default, its backend_url.
Refresh-token authentication
Some portals put password sign-ins behind Cognito's adaptive authentication:
whenever it does not recognise the caller's context, Cognito answers a challenge
(SMS_MFA in practice) instead of tokens. On accounts migrated to such a portal
the pool's phone_number is an unverified placeholder, so the code never arrives
and the challenge can never be answered. The client reports that case as
VeoliaAPIChallengeError rather than a generic authentication failure.
REFRESH_TOKEN_AUTH is not a sign-in flow, so Cognito never risk-scores it: a
refresh token obtained once from a context it already trusts authenticates from
any address. Pass it instead of the credentials, which may then be empty:
client_api = VeoliaAPI("", "", session, refresh_token="ey...")
Obtain the token from a machine the portal is used from, with an
InitiateAuth call carrying the portal's client_id:
curl -s https://cognito-idp.eu-west-3.amazonaws.com/ \
-H 'Content-Type: application/x-amz-json-1.1' \
-H 'X-Amz-Target: AWSCognitoIdentityProviderService.InitiateAuth' \
-d '{"ClientId":"<client_id>","AuthFlow":"USER_PASSWORD_AUTH",
"AuthParameters":{"USERNAME":"your@email.com","PASSWORD":"..."}}' \
| python3 -c 'import json,sys; print(json.load(sys.stdin)["AuthenticationResult"]["RefreshToken"])'
A refresh token is a credential in its own right: store it as you would the password, and keep it out of logs and diagnostics.
It is also short-lived: its validity is set on the Cognito app client, measured
at one hour on the Toulouse Métropole pool, and REFRESH_TOKEN_AUTH returns no
new one — there is no rotation. Treat it as a way to reach an account whose
password sign-in is challenged, not as a lasting alternative to the password.
Cognito's risk score warms up as an address keeps presenting valid tokens, so
the password sign-in is usually accepted again afterwards.
Maintainers can regenerate the portal table from Veolia's national bundle:
python scripts/regenerate_portals.py
The script prints a candidate VEOLIA_PORTALS block and a diff against the
current table — review and edit veolia_api/portals.py manually.
Contributing
Contributions are welcome. Please read CONTRIBUTING.md for guidelines on reporting bugs, suggesting features, and submitting pull requests.
Credits
This repository is inspired by the work done by @CorentinGrard. Thanks to him for his work.
It is a fork of Jezza34000/veolia-api.
License
This project is licensed under the MIT License. See LICENSE for details.
Release files for veolia-api-foxace 2.5.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| veolia_api_foxace-2.5.1.tar.gz | 20.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| veolia_api_foxace-2.5.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 40.9 kB
Release files / veolia_api_foxace-2.5.1.tar.gz
| Download URL | veolia_api_foxace-2.5.1.tar.gz |
|---|---|
| Size | 20.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c9596fce3ab7291b100634ed48cf8431590b86dcee907a7c2a4ec3c379cb19bc
|
|
BLAKE2b-256 checksum How to use checksums |
12a979737d6b5dbbf939d9c01c97af0a37b35f3f5bf84518934c9d758c237847
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 3, 2026.
Transparency logRelease files / veolia_api_foxace-2.5.1-py3-none-any.whl
| Download URL | veolia_api_foxace-2.5.1-py3-none-any.whl |
|---|---|
| Size | 20.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
64174ab6e933ef38ec85c9be585d5e2d02dc8873a4af7729f86d090d44b43796
|
|
BLAKE2b-256 checksum How to use checksums |
07b311dbaa67ce15d03515429f31317870f79bbf1f88f541f9280bff642a75c3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 3, 2026.
Transparency log