Unofficial Python client for the Leapmotor vehicle cloud API
Project description
leapmotor-api
Unofficial Python client for the Leapmotor vehicle cloud API.
Extracted from the leapmotor-ha Home Assistant integration to provide a reusable, framework-agnostic library.
Acknowledgments
Special thanks to Jakob Kern for the impressive reverse engineering work on the Leapmotor application and for generously sharing his work.
Installation
pip install leapmotor-api
Certificates
The library requires the Leapmotor app certificate and private key to authenticate API requests. Download them from the dedicated repository:
wget https://github.com/markoceri/leapmotor-certs/archive/refs/tags/v1.0.0.zip
unzip v1.0.0.zip
The extracted folder contains app_cert.pem and app_key.pem. Pass their paths to the client via app_cert_path and app_key_path.
Quick Start
from leapmotor_api import LeapmotorApiClient
client = LeapmotorApiClient(
username="user@example.com",
password="password",
app_cert_path="/path/to/app_cert.pem",
app_key_path="/path/to/app_key.pem",
)
client.login()
vehicles = client.get_vehicle_list()
for vehicle in vehicles:
status = client.get_vehicle_status(vehicle)
print(f"{vehicle.vin} — Battery: {status.battery.soc}% — Range: {status.battery.expected_mileage} km")
client.close()
Async Usage
from leapmotor_api import LeapmotorApiClient
from leapmotor_api.async_client import AsyncLeapmotorApiClient
sync_client = LeapmotorApiClient(
username="user@example.com",
password="password",
app_cert_path="/path/to/app_cert.pem",
app_key_path="/path/to/app_key.pem",
)
client = AsyncLeapmotorApiClient(sync_client)
await client.login()
vehicles = await client.get_vehicle_list()
status = await client.get_vehicle_status(vehicles[0])
await client.close()
Vehicle Status
get_vehicle_status() returns a VehicleStatus dataclass with typed sub-objects:
| Sub-object | Key fields |
|---|---|
status.battery |
soc, expected_mileage, charge_state, is_charging, is_charge_fast_gun_insert, is_charge_slow_gun_insert, charging_power_kw, battery_power, dump_energy_kwh, charge_plan.* |
status.driving |
speed, total_mileage, gear_status, is_parked |
status.location |
latitude, longitude |
status.climate |
ac_switch, ac_setting, outdoor_temp, interior_temp, ac_operate_mode, ac_air_volume, ac_cooling_and_heating |
status.doors |
is_locked, bbcm_back_door_status |
status.windows |
left_front_window_percent, right_front_window_percent, sun_shade |
status.tires |
front_left_bar, front_right_bar, rear_left_bar, rear_right_bar, all_ok |
status.connectivity |
bluetooth_state, hotspot_state |
status.seat_comfort |
driver_seat_heating, driver_seat_ventilation, steering_wheel_heating |
status.security |
vehicle_security_active, sentry_mode, roof_opening |
status.ignition |
bcm_key_position_on1, bcm_key_position_on3 |
Top-level convenience properties: status.is_locked, status.is_plugged, status.is_charging, status.is_parked, status.is_driving, status.is_regening, status.tire_pressure_bar.
All fields are T | None — they are populated only when the vehicle reports the corresponding signal.
For raw API data, use get_vehicle_raw_status() or access status.raw.
Charging History
from datetime import date, timedelta
result = client.get_charging_daily_detail(
vehicle.vin,
start_time=date.today() - timedelta(days=90),
end_time=date.today(),
timezone="GMT+01:00",
page_size=50,
)
for record in result.records:
charge_kind = "DC (fast)" if record.is_fast_charge else "AC (normal)"
print(f"{record.start_datetime} -> {record.end_datetime} {charge_kind} {record.energy_kwh} kWh")
ChargeRecord fields: start_ts, end_ts, charge_type (ChargeType.AC / ChargeType.DC), energy_kwh, longitude, latitude, timezone.
Convenience properties: start_datetime, end_datetime, duration_seconds, is_fast_charge.
Energy Consumption Statistics
# Last week energy breakdown (driving, A/C, other)
breakdown = client.get_consumption_last_week_breakdown(vehicle)
print(f"Driver: {breakdown.driver_ec} kWh, AC: {breakdown.ac_ec} kWh, Other: {breakdown.other_ec} kWh")
print(f"Total: {breakdown.total_ec} kWh")
# Six-week consumption history and ranking
weekly_rank = client.get_consumption_weekly_rank(vehicle)
print(f"Rank: {weekly_rank.rank.rank}, Avg: {weekly_rank.rank.hundred_km_ec} kWh/100km")
for week in weekly_rank.weekly:
print(f" {week.week_start} ~ {week.week_end}: {week.hundred_km_ec} kWh/100km")
Vehicle Permissions
The Vehicle object exposes the Leapmotor 3-tier permission system as typed enums:
| Field | Type | Description |
|---|---|---|
vehicle.abilities |
list[VehicleAbility] |
Hardware feature flags (what the vehicle can do) |
vehicle.rights |
list[VehicleRight] |
Remote command permissions (what the account can execute) |
vehicle.module_rights |
list[ModuleRight] |
Macro sharing categories |
from leapmotor_api import VehicleAbility, VehicleRight
vehicle = vehicles[0]
# Check capabilities
if vehicle.has_right(VehicleRight.WINDOWS):
client.open_windows(vehicle.vin)
if vehicle.has_ability(VehicleAbility.NAVIGATION):
client.send_destination(vehicle.vin, ...)
# Inspect permissions
for right in vehicle.rights:
print(f"{right.name} ({right.value}): {right.description}")
Remote commands automatically log a warning when the vehicle may lack the required permission, but still proceed (the server enforces permissions authoritatively).
Remote Control
Remote actions require the vehicle PIN (operation_password):
client = LeapmotorApiClient(..., operation_password="1234")
client.login()
client.lock_vehicle("WLM...")
client.unlock_vehicle("WLM...")
client.open_trunk("WLM...")
client.close_trunk("WLM...")
client.find_vehicle("WLM...")
client.hotspot("WLM...")
client.autopark("WLM...")
client.open_windows("WLM...")
client.close_windows("WLM...")
client.ac_on("WLM...")
client.ac_off("WLM...")
client.ac_switch("WLM...")
client.quick_cool("WLM...")
client.quick_heat("WLM...")
client.windshield_defrost("WLM...")
client.open_sunshade("WLM...")
client.close_sunshade("WLM...")
client.battery_preheat("WLM...")
client.battery_preheat_off("WLM...")
client.sentry_mode_on("WLM...")
client.sentry_mode_off("WLM...")
client.start_charging("WLM...")
client.stop_charging("WLM...")
client.steering_wheel_heat_on("WLM...")
client.steering_wheel_heat_off("WLM...")
client.fuel_heating_on("WLM...")
client.fuel_heating_off("WLM...")
client.rearview_mirror_heat_on("WLM...")
client.rearview_mirror_heat_off("WLM...")
client.healthy_charging_on("WLM...")
client.healthy_charging_off("WLM...")
client.on3_on("WLM...")
client.on3_off("WLM...")
client.ble_key_restart("WLM...")
client.music("WLM...", operation="play")
client.video("WLM...", operation="play")
client.fota_download("WLM...", task_id=123)
client.fota_install("WLM...", task_id=123)
client.fota_schedule("WLM...", task_id=123, schedule_time="2026-05-13T10:00:00")
client.rear_seats("WLM...", seat_info="fold")
client.prepare_car("WLM...", params={"temperature": "24"})
# Schedule prepare-car (cmd 361) — full-state replacement; [] cancels all
client.set_prepare_car_schedule("WLM...", controls=[{
"datacontent": {"air_condition": {"mode": "cold", "temperature": "18", "circle": "in",
"windlevel": "7", "wshld": "1", "operate": "manual",
"position": "all", "enable": True}},
"days": [1, 2, 3, 4, 5], "enable": True,
"set_id": "ios_<hash><epoch>", "start_time": "2026-06-17 07:30:00",
}])
client.cancel_prepare_car_schedule("WLM...")
client.seat_adjust("WLM...", params={"position": "driver"})
client.piloted_parking("WLM...", params={"action": "start"})
client.set_speed_limit("WLM...", value="80")
client.seat_heat("WLM...", position=3, level=3)
client.seat_ventilation("WLM...", position=3, level=2)
client.open_sunroof("WLM...")
client.close_sunroof("WLM...")
client.set_charge_limit("WLM...", charge_limit_percent=80)
client.set_charge_schedule(
"WLM...",
enabled=True,
soc_limit=80,
start_time="23:00",
end_time="07:00",
cycles="1,2,3,4,5,6,7",
circulation=1,
)
client.unlock_charger("WLM...")
[!TIP] Consider creating a shared/secondary account in the Leapmotor app and sharing the vehicle with it. This avoids conflicts with your primary account sessions (e.g. being logged out from the phone app or temporary account locks on Leapmotor's servers).
Configuration
| Parameter | Type | Default | Description |
|---|---|---|---|
username |
str |
— | Leapmotor account email |
password |
str |
— | Leapmotor account password |
app_cert_path |
str |
— | Path to app certificate PEM |
app_key_path |
str |
— | Path to app private key PEM |
operation_password |
str | None |
None |
Vehicle PIN (required for remote control) |
language |
str |
"en-GB" |
API language (en-GB, it-IT, de-DE, fr-FR, …) |
verify_ssl |
bool |
False |
Verify server TLS certificate |
base_url |
str |
DEFAULT_BASE_URL |
API base URL |
timeout |
int |
30 |
HTTP timeout in seconds |
device_id |
str | None |
None |
Custom device ID (auto-generated if omitted) |
Token refresh is handled automatically — all methods detect expired tokens and transparently refresh or re-login. See token_refresh() for manual control.
Documentation
- API Reference — endpoints, cryptography and remote commands
- Vehicle Model Differences — API response format and signal IDs across vehicle models
Contributing
Interested in contributing? Read the contributing guide for development setup, testing, and PR guidelines.
License
This project is licensed under the GNU Affero General Public License v3.0.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file leapmotor_api-0.3.2.tar.gz.
File metadata
- Download URL: leapmotor_api-0.3.2.tar.gz
- Upload date:
- Size: 110.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5f9c06aee394958fbbba3365184fd1f2666f34ba41805aa1cc5776564d42568c
|
|
| MD5 |
90085fd003fe4635e39d2c19de21ace6
|
|
| BLAKE2b-256 |
20fcc7dfb1e8d52db674df0438ee79bce253005118a1a97a099a41ea3894a1c7
|
Provenance
The following attestation bundles were made for leapmotor_api-0.3.2.tar.gz:
Publisher:
publish.yml on markoceri/leapmotor-api
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
leapmotor_api-0.3.2.tar.gz -
Subject digest:
5f9c06aee394958fbbba3365184fd1f2666f34ba41805aa1cc5776564d42568c - Sigstore transparency entry: 1903412772
- Sigstore integration time:
-
Permalink:
markoceri/leapmotor-api@27443fce118d88b84dddab1a402c8a844a3c4126 -
Branch / Tag:
refs/tags/v0.3.2 - Owner: https://github.com/markoceri
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@27443fce118d88b84dddab1a402c8a844a3c4126 -
Trigger Event:
release
-
Statement type:
File details
Details for the file leapmotor_api-0.3.2-py3-none-any.whl.
File metadata
- Download URL: leapmotor_api-0.3.2-py3-none-any.whl
- Upload date:
- Size: 64.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
13352de8552276b2a739bc8afb19e7bd863868fc7f50b09dccec7373cb34d2f2
|
|
| MD5 |
cfdf320d68feefb18ebe6c8b3b0a65a2
|
|
| BLAKE2b-256 |
6019e7d2d18666b44a40f4dbe84390cd5cb1e8a0b3c2688112f8f2f8816bc390
|
Provenance
The following attestation bundles were made for leapmotor_api-0.3.2-py3-none-any.whl:
Publisher:
publish.yml on markoceri/leapmotor-api
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
leapmotor_api-0.3.2-py3-none-any.whl -
Subject digest:
13352de8552276b2a739bc8afb19e7bd863868fc7f50b09dccec7373cb34d2f2 - Sigstore transparency entry: 1903412995
- Sigstore integration time:
-
Permalink:
markoceri/leapmotor-api@27443fce118d88b84dddab1a402c8a844a3c4126 -
Branch / Tag:
refs/tags/v0.3.2 - Owner: https://github.com/markoceri
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@27443fce118d88b84dddab1a402c8a844a3c4126 -
Trigger Event:
release
-
Statement type: