Skip to main content

leapmotor-api

PyPI version Python versions License Downloads CI codecov Ruff mypy Ko-fi

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, latitude_signed, longitude_signed
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_kpa, front_right_kpa, rear_left_kpa, rear_right_kpa (raw), *_bar, *_psi, all_bar, all_psi, 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, status.tire_pressure_psi.

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")

Both cloud statistics also accept an arbitrary window. Pass timezone-aware datetimes; the client converts them to the unit each endpoint expects:

from datetime import datetime, timedelta, timezone

end = datetime.now(timezone.utc)
start = end - timedelta(days=7)

# Energy split for any window (resolution goes down to about 15-20 minutes)
breakdown = client.get_consumption_breakdown(vehicle, start=start, end=end)

# Lifetime energy plus per-day mileage and energy over the window
history = client.get_mileage_energy_history(vehicle, start=start, end=end)
print(f"Lifetime energy: {history.total_energy_kwh} kWh over {history.total_mileage_km} km")
for day in history.days:
    print(f"  {day.day}: {day.mileage_km} km, {day.energy_kwh} kWh")

total_energy_kwh is the precise lifetime figure (standby included). The per-day energy_kwh is coarse: the cloud often rounds it to an integer.

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).

Car Sharing

A car the owner shares from the official app reaches the second account as an invitation. Until it is accepted the car is missing from get_vehicle_list() and every vehicle call fails with No such permission. The recipient side is exposed here:

for invitation in client.get_share_invitations():
    print(invitation.vin, invitation.car_type, invitation.owner_nickname, invitation.module_rights)
    client.accept_share_invitation(invitation)   # or client.reject_share_invitation(invitation)

# The car now shows up as a shared vehicle, with the rights the owner granted
shared = [v for v in client.get_vehicle_list() if v.is_shared]

Creating or revoking a share (the owner side) is not covered.

Software Updates (OTA)

get_software_info() returns the installed software version and, when the cloud offers one, the OTA update waiting to be installed, with its release notes and packages. The information is available only to the account that owns the car: for an account the car is shared with, the cloud answers No such permission and the call raises LeapmotorPermissionError.

from leapmotor_api import LeapmotorPermissionError

try:
    software = client.get_software_info(vehicle)
except LeapmotorPermissionError:
    print("Only the owner account sees the software version")
else:
    if software.update_available:
        new = software.new_version
        print(f"Update {new.version}: {new.download_size / 1e9:.2f} GB in {len(new.packages)} packages")
        for note in new.release_notes_for("it-IT"):
            print(f"- {note.title}: {note.content}")

Each SoftwarePackage carries a signed download_url; it is kept out of repr(), keep it out of logs too.

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...")

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

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.

Metadata

Release files for leapmotor-api 0.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for leapmotor-api 0.5.0
File Size Uploaded
leapmotor_api-0.5.0.tar.gz 133.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for leapmotor-api 0.5.0
File Interpreter ABI Platform
leapmotor_api-0.5.0-py3-none-any.whl Python 3 none any Details

Total release size: 207.7 kB

Release files / leapmotor_api-0.5.0.tar.gz

Download URL leapmotor_api-0.5.0.tar.gz
Size 133.6 kB
Tags Source
SHA-256 checksum
How to use checksums
a3143207cabed2014fc41ec2b46c235b435173a00d1eb3439d3d0e066e818f7d
BLAKE2b-256 checksum
How to use checksums
41298ffa4140b408d24652bb05a8cb70e703e21c7761d0b25e08cccd35aace4f
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 Oct 13, 2026.

Transparency log

Release files / leapmotor_api-0.5.0-py3-none-any.whl

Download URL leapmotor_api-0.5.0-py3-none-any.whl
Size 74.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1f711dc294782fda34e81bcbcb649161d50f17ed2251595beed3e7a140363df5
BLAKE2b-256 checksum
How to use checksums
16ddec8f6c604a319ec6e1a3131c3392d8ac94239ed60dd6f018a16d716fd6f5
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 Oct 13, 2026.

Transparency log

Release history Release notifications | RSS feed

0.6.0

2 release files

This release

0.5.0 This release

2 release files

0.4.0

2 release files

0.3.3

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.7

2 release files

0.1.6

2 release files

0.1.5

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page