Python client and REST API for the Audi Connect (myAudi) service. Check your vehicle status and execute remote actions via CLI or HTTP API.
Based on the open-source audiconnect/audi_connect_ha project.
Disclaimer: For personal and educational use only. Not affiliated with, endorsed by, or sponsored by Audi AG or Volkswagen Group. Use at your own risk — usage of the Audi Connect service is subject to Audi AG's terms of service.
⚠️ Rate limits: Audi's API is known to enforce aggressive rate limits (~6 requests/hour). Excessive polling will temporarily lock your account and the official myAudi app until the window resets. The default settings here are conservative (4h cache, background watcher disabled, 15 min minimum poll interval) — don't override them unless you know what you're doing.
- Full vehicle status (mileage, range, fuel level, oil level, AdBlue)
- Quick status mode — locked, position, range in one glance
- GPS position with Google Maps link (auto-open in browser)
- Door, trunk, hood and window states
- Charging data (EV/PHEV): SoC, power, remaining time, plug state
- Remote lock / unlock (requires S-PIN) with confirmation
- Remote climate control (start/stop, 16-30°C)
- Auxiliary heater (start/stop, 10-60 min)
- Trip data (short-term and long-term)
- REST API (FastAPI) with Docker deployment and rate limiting
- Watch mode — monitor changes and send webhook notifications
- Action retry — automatic retry on network failures (lock, climate, heater)
- Home Assistant integration via command_line sensor
- OAuth token caching to avoid re-authenticating on every run
- Interactive setup (
python main.py setup)
In-depth technical reference is in docs/:
- Architecture — module layout, layered design
- OAuth flow — the 13-step authentication
- Auth lifecycle — refresh vs login
- API reference — REST + CLI
- Observability — metrics, health, logs
- Deployment — K8s + ArgoCD + secrets
- Development — tests, patterns, pitfalls
git clone https://github.com/John6810/myaudi-api.git
cd myaudi-api
pip install -r requirements.txt
python main.py setup # Interactive — creates .env file
python main.py status --brief # Quick checkRun the interactive setup to create your .env file:
python main.py setupOr copy the template:
cp .env.example .envOr create .env manually:
AUDI_USERNAME=your.email@example.com
AUDI_PASSWORD=your_password
AUDI_COUNTRY=DE
AUDI_SPIN=1234
AUDI_API_LEVEL=1
AUDI_DEFAULT_VIN=WAUXXXXXXXXXXXXX
AUDI_WEBHOOK_URL=https://n8n.example.com/webhook/audi| Variable | Description | Default |
|---|---|---|
AUDI_USERNAME |
myAudi account email | (required) |
AUDI_PASSWORD |
Password | (required) |
AUDI_COUNTRY |
Country code (DE, FR, US, etc.) | DE |
AUDI_SPIN |
S-PIN for lock/unlock | (optional) |
AUDI_API_LEVEL |
0 = legacy MBB, 1 = new CARIAD API |
1 |
AUDI_DEFAULT_VIN |
Default VIN — skip --vin for single-vehicle users |
(optional) |
AUDI_WEBHOOK_URL |
Webhook URL for state change notifications | (optional) |
AUDI_WEBHOOK_SECRET |
If set, webhooks are signed with HMAC-SHA256 in the X-Audi-Signature header |
(optional) |
AUDI_WATCH_INTERVAL |
Background poll interval in seconds (API server only) | 0 (disabled) |
AUDI_CACHE_TTL |
Data cache TTL in seconds | 14400 (4h) |
AUDI_API_KEY |
Required header X-API-Key on all endpoints except /health, /ready, /metrics. Set to a strong random token. |
(optional but strongly recommended) |
# Quick status (locked, position, range)
python main.py status --brief
# Full vehicle status (with debug logs)
python main.py status -v
# GPS position (opens in browser)
python main.py position --open-maps
# Lock with confirmation (waits 5s and checks)
python main.py lock --confirm
# Unlock
python main.py unlock --confirm
# Climate control
python main.py climate-start --temp 22
python main.py climate-stop
# Auxiliary heater
python main.py heater-start --duration 30
python main.py heater-stop
# Watch mode — monitor changes, send webhooks
python main.py watch --interval 900
# Target a specific VIN (if multiple vehicles)
python main.py status --vin WAUXXXXXXXXXXXXXuvicorn server:app --host 0.0.0.0 --port 8000docker build -t audi-connect .
docker run -p 8000:8000 \
-e AUDI_USERNAME="your.email@example.com" \
-e AUDI_PASSWORD="your_password" \
-e AUDI_COUNTRY="DE" \
-e AUDI_SPIN="1234" \
-e AUDI_API_LEVEL="1" \
audi-connectkubectl create secret generic audi-credentials \
-n myaudi-api \
--from-literal=AUDI_USERNAME="your.email@example.com" \
--from-literal=AUDI_PASSWORD="your_password" \
--from-literal=AUDI_COUNTRY="DE" \
--from-literal=AUDI_SPIN="1234" \
--from-literal=AUDI_API_LEVEL="1"All endpoints except /health require an X-API-Key header.
Set AUDI_API_KEY server-side (in the K8s secret or your .env):
# Generate a strong key
python -c "import secrets; print(secrets.token_urlsafe(32))"Add it to your .env along with the other required vars:
AUDI_USERNAME=your.email@example.com
AUDI_PASSWORD=your_password
AUDI_COUNTRY=DE
AUDI_SPIN=1234
AUDI_API_LEVEL=1
AUDI_API_KEY=<generated_key>Send the same key as a header on every call:
curl -H "X-API-Key: your_key_here" http://localhost:8000/briefWithout the header (or with a wrong key), endpoints return 401. If AUDI_API_KEY is unset on the server, all protected endpoints return 503.
GET /metrics exposes Prometheus metrics in text format. In addition to the standard FastAPI HTTP metrics, four custom business metrics are emitted:
audi_auth_refresh_total{result="success|failure"}— login/refresh attemptsaudi_cache_operation_total{operation="hit|miss|invalidate"}— cache eventsaudi_action_total{action, result}— remote actions countsaudi_backend_request_duration_seconds{endpoint}— upstream Audi latency
The /metrics, /health and /ready endpoints are excluded from HTTP request metrics to avoid kubelet probes drowning the signal.
GET /health— liveness. Returns 200 even when Audi Connect is degraded (don't kill the pod just because upstream is down).GET /ready— readiness. Returns 503 until the service has successfully authenticated to Audi Connect.
Every request gets an X-Request-ID header (provided by the client if present, otherwise generated). The same value is included in log records under [rid=...] so requests can be correlated end-to-end in Loki/Grafana.
| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/health |
public | Liveness check + cache info. 200 even when Audi is degraded. |
GET |
/ready |
public | Readiness probe. 503 until authenticated to Audi Connect. |
GET |
/metrics |
public | Prometheus metrics in text format. |
GET |
/vehicles |
X-API-Key | List vehicles |
GET |
/status |
X-API-Key | Full vehicle status (optional ?vin=) |
GET |
/brief |
X-API-Key | Quick status: locked, position, range |
GET |
/position |
X-API-Key | GPS position (optional ?vin=) |
POST |
/{vin}/lock |
X-API-Key | Lock vehicle (?confirm=true to verify) |
POST |
/{vin}/unlock |
X-API-Key | Unlock vehicle (?confirm=true to verify) |
POST |
/{vin}/climate/start |
X-API-Key | Start climate (?temp=21&confirm=true) |
POST |
/{vin}/climate/stop |
X-API-Key | Stop climate |
POST |
/{vin}/heater/start |
X-API-Key | Start heater (?duration=30&confirm=true) |
POST |
/{vin}/heater/stop |
X-API-Key | Stop heater |
Action endpoints accept ?confirm=true to wait 5 seconds after the action, re-fetch vehicle data, and return the updated status. Without it, the response is immediate ("status": "sent").
Cache is automatically invalidated after any action so the next GET /status reflects the change.
Rate limiting: read endpoints allow 30 requests/min, action endpoints allow 5 requests/min. Exceeding returns HTTP 429. The /health, /ready and /metrics endpoints are not rate-limited and don't require X-API-Key — they are intended for kubelet probes and Prometheus scraping.
curl http://localhost:8000/brief{
"vehicles": [{
"vehicle": "My Audi",
"locked": "Locked",
"position": "50.123, 4.456",
"maps": "https://www.google.com/maps?q=50.123,4.456",
"range": "200 km",
"battery": "51%"
}]
}curl http://localhost:8000/status{
"count": 1,
"vehicles": [{
"vin": "WAUXXXXX",
"mileage": "4,391 km",
"range": "200 km",
"battery_soc": "51%",
"charging": "notReadyForCharging",
"doors_trunk": "Locked",
"windows": "Closed",
"climatisation": "off"
}]
}curl -X POST "http://localhost:8000/WAUXXXXX/lock?confirm=true"{
"status": "confirmed",
"action": "lock",
"vin": "WAUXXXXX",
"vehicle_status": {"doors_trunk": "Locked", "windows": "Closed", "range": "200 km"}
}Set AUDI_WEBHOOK_URL and AUDI_WATCH_INTERVAL to enable background monitoring. The API server will poll vehicle data at the configured interval and POST state changes:
{
"event": "state_change",
"vin": "WAUXXXXX",
"title": "My Audi",
"changes": {
"locked": {"old": "Locked", "new": "Closed"}
},
"state": {"vehicle": "My Audi", "locked": "Closed", "position": "50.123, 4.456", "range": "200 km"},
"timestamp": "2026-04-12T22:30:00+02:00"
}Works with n8n, Home Assistant webhooks, Discord webhooks, or any HTTP endpoint.
Set AUDI_WEBHOOK_SECRET to enable HMAC-SHA256 signing. Each request will include an X-Audi-Signature: sha256=<hex> header computed over the raw request body. The receiver must recompute the HMAC over the raw body and reject mismatches.
Example n8n verification (Function node — webhook node must be configured to use raw body, otherwise n8n re-serialises the payload and the signature won't match):
const crypto = require('crypto');
const secret = $env.AUDI_WEBHOOK_SECRET;
const sig = $request.headers['x-audi-signature'];
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update($request.body.raw)
.digest('hex');
if (!sig || !crypto.timingSafeEqual(
Buffer.from(sig), Buffer.from(expected))) {
throw new Error('Invalid signature');
}
return items;The ha_sensor.py script outputs vehicle data as JSON to stdout, compatible with Home Assistant's command_line sensor:
command_line:
- sensor:
name: "Audi Connect"
command: "python3 /config/scripts/myaudi-api/ha_sensor.py"
value_template: "{{ value_json.range }}"
json_attributes:
- vin
- model
- mileage
- range
- battery_soc
- charging_state
- plug_state
- latitude
- longitude
- doors_locked
- windows_closed
- climatisation
scan_interval: 300myaudi-api/
server.py # FastAPI REST API (cache, webhooks, confirm)
main.py # CLI (setup, status, watch, actions)
ha_sensor.py # Home Assistant script
Dockerfile
requirements.txt
audi_connect/
oauth.py # 13-step OAuth2/OIDC login flow
oauth_state.py # Frozen OAuthState dataclass (10 token fields, from_dict / to_dict / with_refresh)
auth.py # Token coordinator (delegates to oauth.py / client / actions)
endpoints.py # cariad_url() helper + AudiEndpoints (URL building + home-region cache)
client.py # Read-only API calls (status, position, trips)
actions.py # Remote actions (lock, climate, heater)
api.py # Low-level HTTP client (retry, timeout)
connection.py # Shared connection helpers
vehicle.py # AudiVehicle (properties, validation, brief/dashboard, idempotent-only retry)
watcher.py # Shared watch logic (diff_states, check_vehicles callbacks)
models.py # Response parsing + LockState/DoorState/WindowState enums
utils.py # Utility functions
exceptions.py # Custom exceptions
token_store.py # Token persistence (restricted file permissions)
logging_utils.py # redact() + RedactingFilter (masks bearer tokens, OAuth JSON, X-QMAuth, emails in logs)
tests/ # 183 tests (pytest + pytest-asyncio + aioresponses)
test_vehicle.py # is_moving, parallel fetch, validation, brief, idempotent retry policy
test_actions.py # S-PIN hash, climate (CARIAD + legacy), heater
test_auth.py # Token restore, login (returns vehicle list), refresh, OAuth helpers
test_integration.py # Integration tests (real HTTP stack, mocked network)
test_watcher.py # State diff, vehicle polling callbacks
test_models.py # Response parsing, enums, indexed get_field/get_state
test_cli.py # Error formatting, VIN resolution
test_utils.py # Utility functions
test_token_store.py # OAuthState round-trip persistence
test_exceptions.py # Exception hierarchy
test_endpoints.py # cariad_url + AudiEndpoints home-region cache
test_logging_utils.py # redact patterns + RedactingFilter
test_observability.py # /metrics, /ready, X-Request-ID middleware
test_api_auth.py # X-API-Key dependency on protected endpoints
.github/workflows/
build.yml # CI/CD: Docker build + GHCR push + GitOps update
python -m pytest tests/ -v183 tests covering: authentication flow, OAuth helpers, OAuthState dataclass + token persistence, vehicle data parsing, action validation, idempotent-only retry policy, parallel fetching, error formatting, enums, state watcher, integration tests with mocked HTTP, URL building / home-region cache, log secret redaction, X-API-Key dependency, /metrics + /ready + request-id middleware.
Authentication implements the OAuth2/OIDC flow used by the myAudi service in 13 steps:
- Fetch Audi market configuration
- OpenID Connect discovery
- PKCE challenge generation (S256)
- Email + password submission via HTML forms
- Exchange authorization code for an IDK bearer token
- Obtain the AZS (Audi) token
- Register MBB OAuth client (VW Group)
- Obtain and refresh the MBB token
Three tokens are managed in parallel:
- IDK/CARIAD: for the new vehicle API
- AZS: for the Audi GraphQL API (vehicle list)
- MBB/VW: for the legacy API (trips, lock/unlock)
Tokens are cached locally (~/.audi_connect_tokens.json, 1h TTL, restricted file permissions on Unix) to skip the full login flow on subsequent runs. The API server refreshes tokens automatically every 45 minutes.
aiohttp- Async HTTP clientbeautifulsoup4- HTML parsing (authentication flow)python-dotenv-.envfile loadingcertifi- CA bundle for SSL validationtenacity- Retry with exponential backofffastapi- REST API frameworkuvicorn- ASGI serverslowapi- Rate limitingprometheus-fastapi-instrumentator- Standard FastAPI HTTP metrics + custom business metrics on/metricspytest+pytest-asyncio+aioresponses- Testing
MIT License. See LICENSE for details.
Usage of the Audi Connect API is subject to Audi AG's terms of service.