Skip to main content

Tesla Fleet API

Tesla Fleet API is a Python library that provides an interface to interact with Tesla's Fleet API, including signed commands and encrypted local Bluetooth (BLE) communication. It also supports interactions with Teslemetry and Tessie services.

Features

  • Fleet API for vehicles
  • Fleet API for energy sites
  • Fleet API with signed vehicle commands
  • Bluetooth for vehicles
  • Routing and failover across backends for vehicles and energy sites (e.g. Bluetooth/local primary, cloud fallback)
  • Teslemetry integration
  • Tessie integration

Installation

You can install the library using pip:

pip install tesla-fleet-api

Usage

Authentication

The TeslaFleetOAuth class provides methods that help with authenticating to the Tesla Fleet API. Here's a basic example:

import asyncio
import aiohttp
from tesla_fleet_api import TeslaFleetOAuth

async def main():
    async with aiohttp.ClientSession() as session:
        oauth = TeslaFleetOAuth(
            session=session,
            client_id="<client_id>",
            client_secret="<client_secret>",
            redirect_uri="<redirect_uri>",
        )

        # Get the login URL and navigate the user to it
        login_url = oauth.get_login_url(scopes=["openid", "email", "offline_access"])
        print(f"Please go to {login_url} and authorize access.")

        # After the user authorizes access, they will be redirected to the redirect_uri with a code
        code = input("Enter the code you received: ")

        # Exchange the code for a refresh token
        await oauth.get_refresh_token(code)
        print(f"Access token: {oauth.access_token}")
        print(f"Refresh token: {oauth.refresh_token}")
        # Dont forget to store the refresh token so you can use it again later

asyncio.run(main())

Fleet API for Vehicles

The TeslaFleetApi class provides methods to interact with the Fleet API for vehicles. Here's a basic example:

import asyncio
import aiohttp
from tesla_fleet_api import TeslaFleetApi
from tesla_fleet_api.exceptions import TeslaFleetError

async def main():
    async with aiohttp.ClientSession() as session:
        api = TeslaFleetApi(
            access_token="<access_token>",
            session=session,
            region="na",
        )

        try:
            data = await api.vehicles.list()
            print(data)
        except TeslaFleetError as e:
            print(e)

asyncio.run(main())

For more detailed examples, see Fleet API for Vehicles.

Fleet API for Energy Sites

The EnergySites class provides methods to interact with the Fleet API for energy sites. Here's a basic example:

import asyncio
import aiohttp
from tesla_fleet_api import TeslaFleetApi
from tesla_fleet_api.exceptions import TeslaFleetError

async def main():
    async with aiohttp.ClientSession() as session:
        api = TeslaFleetApi(
            access_token="<access_token>",
            session=session,
            region="na",
        )

        try:
            energy_sites = await api.energySites.list()
            print(energy_sites)
        except TeslaFleetError as e:
            print(e)

asyncio.run(main())

For more detailed examples, see Fleet API for Energy Sites.

To pair an energy gateway's RSA key over the cloud and compose the resulting signed local LAN control (via the sibling aiopowerwall library) with a cloud fallback through EnergySiteRouter, see Energy: Local Control.

Fleet API with Signed Vehicle Commands

The VehicleSigned class provides methods to interact with the Fleet API using signed vehicle commands. Here's a basic example:

import asyncio
import aiohttp
from tesla_fleet_api import TeslaFleetApi
from tesla_fleet_api.tesla.vehicle.signed import VehicleSigned
from tesla_fleet_api.exceptions import TeslaFleetError

async def main():
    async with aiohttp.ClientSession() as session:
        api = TeslaFleetApi(
            access_token="<access_token>",
            session=session,
            region="na",
        )

        try:
            vehicle = VehicleSigned(api, "<vin>")
            data = await vehicle.wake_up()
            print(data)
        except TeslaFleetError as e:
            print(e)

asyncio.run(main())

For more detailed examples, see Fleet API with Signed Vehicle Commands.

Bluetooth for Vehicles

The TeslaBluetooth class provides methods to interact with Tesla vehicles using Bluetooth. Here's a basic example:

import asyncio
from bleak import BleakScanner
from tesla_fleet_api import TeslaBluetooth

async def main():
    scanner = BleakScanner()
    devices = await scanner.discover()
    for device in devices:
        if TeslaBluetooth().valid_name(device.name):
            print(f"Found Tesla vehicle: {device.name}")

asyncio.run(main())

For more detailed examples, see Bluetooth for Vehicles.

get_private_key(path) loads an existing EC private key or creates a new unencrypted PEM key file, and get_rsa_private_key(path) does the same for an RSA key. Newly created key files are created owner-readable and owner-writable only (0600) from the start, with no write-then-chmod window, and concurrent creators fall back to reading the file that won the create race. If an existing key file can't be read, isn't valid PEM, is password-encrypted, or is the wrong key type, both raise PrivateKeyError (a LibraryError, not a TeslaFleetError - it's a local key-file failure, not an upstream Fleet API error) with a reason of "unreadable", "malformed", "encrypted", or "wrong_type".

VehicleBluetooth keeps a held BLE connection alive during idle periods by default with a passive GATT read about every 20 seconds. Pass keepalive_interval=None (or 0) when creating the vehicle to disable it; leaving it enabled can keep an already-awake car awake longer, so disconnect or disable keepalive when vehicle sleep is preferred.

BLE connect/notify failures, and GATT writes rejected before backend I/O, raise BluetoothTransportError, a TeslaFleetError subclass, with the original transport exception chained as __cause__ when available. A GATT write that entered backend I/O and then failed or timed out is delivery-ambiguous and raises BluetoothTimeout/BluetoothUnconfirmedCommand instead. Mutating BLE commands use a confirmation ladder controlled by confirmation ("ack" by default) and raise_unconfirmed (False by default): an inconclusive lost acknowledgement resolves as best-effort success unless you opt in to BluetoothUnconfirmedCommand, while a command proven not to have applied raises BluetoothCommandFailed. See Bluetooth for Vehicles for the full ladder. Catch TeslaFleetError to handle Bluetooth transport failures (including bleak.exc.BleakError and builtin TimeoutError from ESPHome proxies) and response-wait timeouts through the same library error hierarchy.

VehicleBluetooth can also register persistent BLE broadcast listeners for unsolicited VCSEC VehicleStatus updates. Use typed listen_* helpers for the decoded vehicle-status fields, or listen_broadcast(domain, callback) for raw per-domain broadcast messages. Use listen_connection_status(callback) for True/False BLE session transition notifications. See Bluetooth for Vehicles for the connection-event contract.

Routing and Failover

The Router class composes an ordered list of two-or-more backends that share a common method surface and dispatches each method call down the chain, automatically failing over on most errors. VehicleRouter and EnergySiteRouter are thin entity-specific subclasses. A common setup is a local VehicleBluetooth primary with a cloud fallback (e.g. a TeslemetryVehicle), so commands go over Bluetooth when the vehicle is reachable and route to the cloud otherwise:

import asyncio
import aiohttp
from tesla_fleet_api import TeslaBluetooth, Teslemetry
from tesla_fleet_api.router import VehicleRouter
from tesla_fleet_api.exceptions import TeslaFleetError

async def main():
    async with aiohttp.ClientSession() as session:
        # Primary: local Bluetooth
        tesla_bluetooth = TeslaBluetooth()
        await tesla_bluetooth.get_private_key("path/to/private_key.pem")
        primary = tesla_bluetooth.vehicles.create("<vin>", confirmation="verify")

        # Secondary (fallback): Teslemetry cloud
        teslemetry = Teslemetry(access_token="<access_token>", session=session)
        secondary = teslemetry.vehicles.create("<vin>")

        vehicle = VehicleRouter(primary, secondary)

        try:
            await vehicle.wake_up()
        except TeslaFleetError as e:
            print(e)

asyncio.run(main())

The constructor is Router(primary, secondary, *more_backends, health=None, on_error=None); the two-argument form shown above is fully backward compatible, and any number of extra backends may follow to extend the chain. Each call is tried on the first backend that has the method and, on any exception except BluetoothUnconfirmedCommand, retried on the next backend that has it, returning the first success (raising the last error only if every applicable backend fails). Non-callable attributes (e.g. vin) resolve to the first backend that has them.

By default the router attempts the primary and fails over on any error, with no up-front probe. You can also pass an explicit health check — a bool, a sync callable, or an async callable returning bool — to decide up front whether to route to the primary or skip straight to the rest of the chain. The health check gates only the primary (the first backend); later backends are reached purely through per-command failover.

You can also pass on_error — a sync or async callable (exception, backend, method_name) -> bool — to hook into every dispatched call's outcome, success or failure. On a backend exception during failover (every one except BluetoothUnconfirmedCommand) returning True lets failover continue to the next backend as normal, while False stops it and re-raises that exception immediately, so a command already known to fail on the next backend never gets sent there. On a successful call it's called the same way with exception=None and its return value ignored — the only hook available to observe a dispatch succeeding, e.g. to clear a repair a prior failure raised. For example, treating a BLE key rejection as terminal instead of falling over to the cloud, and clearing the repair once a command succeeds again:

from tesla_fleet_api.exceptions import is_key_rejected

def on_error(exc, backend, method_name):
    if exc is None:
        clear_repair(backend)  # a dispatched call just succeeded
        return True
    if is_key_rejected(exc):
        raise_repair(backend)  # your own repair/notification logic
        return False  # don't send this command to the cloud too
    return True

vehicle = VehicleRouter(primary, secondary, on_error=on_error)

tesla_fleet_api.exceptions.is_key_rejected(exc) reports whether a fault means the vehicle didn't recognize our signing key as paired/authorized (e.g. NotOnWhitelistFault), as opposed to any other signed-command fault.

EnergySiteRouter follows the same pattern for energy sites, pairing a duck-typed local EnergySite-shaped object (e.g. aiopowerwall's PowerwallEnergySite, no dependency added) with a cloud TeslemetryEnergySite fallback:

from tesla_fleet_api.router import EnergySiteRouter

router = EnergySiteRouter(local_energysite, teslemetry_energysite)
await router.operation(...)  # local first, cloud on failure

Router, VehicleRouter, and EnergySiteRouter are all importable from tesla_fleet_api.router (and, for backward compatibility, from tesla_fleet_api.tesla).

Enable DEBUG logging for tesla_fleet_api to see which backend served a routed call and why failover happened.

Warning: Because a failed call is replayed on the next backend, a non-idempotent command (e.g. honk_horn, actuate_trunk, door_unlock, charge_start) that fails mid-flight — after a backend may have already partially applied it — can be double-executed (or executed more than once across a longer chain) when it is retried on the next backend. This is a deliberate tradeoff of per-command failover. BluetoothUnconfirmedCommand is the exception: it propagates without failover because the BLE command may already have executed. When the primary is VehicleBluetooth, pass confirmation="verify" to resolve supported mutating command timeouts by state before they reach the router, and set raise_unconfirmed=True when callers must see still-ambiguous outcomes instead of the default best-effort success; callers needing exactly-once semantics for other commands should gate dispatch with an explicit health check or call the underlying backends directly.

Dispatch is implemented via __getattr__, which does not proxy dunder methods, so async with Router(...) does not manage a backend's BLE connection lifecycle (__aenter__/__aexit__). Commands still auto-connect on send; for explicit connect/disconnect reach through router.primary (or router.backends).

Debug Logging

Enable the tesla_fleet_api logger at DEBUG to see each command's command name, transport/backend, and result. In standalone scripts, configure a handler first:

import logging

logging.basicConfig(level=logging.DEBUG)
logging.getLogger("tesla_fleet_api").setLevel(logging.DEBUG)

Command log lines use transport=bluetooth, fleet, teslemetry, or tessie. Routers also emit backend=<ClassName> lines for each backend tried. See Bluetooth for Vehicles for examples and the signed-command naming details. REST responses that are valid JSON but not objects, such as null, lists, or scalars, are returned unchanged and log as result=success.

Teslemetry

The Teslemetry class provides methods to interact with the Teslemetry service. Here's a basic example:

import asyncio
import aiohttp
from tesla_fleet_api import Teslemetry
from tesla_fleet_api.exceptions import TeslaFleetError

async def main():
    async with aiohttp.ClientSession() as session:
        api = Teslemetry(
            access_token="<access_token>",
            session=session,
        )

        try:
            data = await api.vehicles.list()
            print(data)
        except TeslaFleetError as e:
            print(e)

asyncio.run(main())

For more detailed examples, see Teslemetry.

Tessie

The Tessie class provides methods to interact with the Tessie service. Here's a basic example:

import asyncio
import aiohttp
from tesla_fleet_api import Tessie
from tesla_fleet_api.exceptions import TeslaFleetError

async def main():
    async with aiohttp.ClientSession() as session:
        api = Tessie(
            access_token="<access_token>",
            session=session,
        )

        try:
            data = await api.vehicles.list()
            print(data)
        except TeslaFleetError as e:
            print(e)

asyncio.run(main())

For more detailed examples, see Tessie.

Release files for tesla-fleet-api 1.13.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 tesla-fleet-api 1.13.0
File Size Uploaded
tesla_fleet_api-1.13.0.tar.gz 237.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for tesla-fleet-api 1.13.0
File Interpreter ABI Platform
tesla_fleet_api-1.13.0-py3-none-any.whl Python 3 none any Details

Total release size: 381.9 kB

Release files / tesla_fleet_api-1.13.0.tar.gz

Download URL tesla_fleet_api-1.13.0.tar.gz
Size 237.5 kB
Tags Source
SHA-256 checksum
How to use checksums
24fb7fc76266da5d3c377d360c5b91a5ed0230f918000f983f3bafb5d8f65156
BLAKE2b-256 checksum
How to use checksums
7b7df516f33ad8eef5f30b9b253e96b9f660c0adb65d5e08d3e85f25243eefc9
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 14, 2026.

Transparency log

Release files / tesla_fleet_api-1.13.0-py3-none-any.whl

Download URL tesla_fleet_api-1.13.0-py3-none-any.whl
Size 144.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a405641e8f5f2a649ea7e56692844499dfe81fe3a776a48e705ec7f5831823a6
BLAKE2b-256 checksum
How to use checksums
3289d5fc47efc9789151d1c20036f7ba68a274cb1d566e82796225d80a57f5ef
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 14, 2026.

Transparency log

Release history Release notifications | RSS feed

1.15.0

2 release files

1.14.1

2 release files

1.14.0

2 release files

This release

1.13.0 This release

2 release files

1.12.1

2 release files

1.12.0

2 release files

1.11.1

2 release files

1.11.0

2 release files

1.10.1

2 release files

1.10.0

2 release files

1.9.0

2 release files

1.8.2

2 release files

1.8.0

2 release files

1.7.6

2 release files

1.7.5

2 release files

1.7.4

2 release files

1.7.3

2 release files

1.7.2

2 release files

1.7.1

2 release files

1.7.0

2 release files

1.6.4

2 release files

1.6.3

2 release files

1.6.2

2 release files

1.6.1

2 release files

1.6.0

2 release files

1.5.4

2 release files

1.5.3

2 release files

1.5.2

2 release files

1.5.1

2 release files

1.5.0

2 release files

1.4.7

2 release files

1.4.6

2 release files

1.4.5

2 release files

1.4.3

2 release files

1.4.2

2 release files

1.4.1

2 release files

1.4.0

2 release files

1.3.2

2 release files

1.3.0

2 release files

1.2.7

2 release files

1.2.6

2 release files

1.2.5

2 release files

1.2.4

2 release files

1.2.3

2 release files

1.2.2

2 release files

1.2.1

2 release files

1.2.0

2 release files

1.1.3

2 release files

1.1.2

2 release files

1.1.1

2 release files

1.1.0

2 release files

1.0.16

2 release files

1.0.15

2 release files

1.0.14

2 release files

1.0.13

2 release files

1.0.9

2 release files

1.0.8

2 release files

1.0.7

2 release files

1.0.6

2 release files

1.0.5

2 release files

1.0.4

2 release files

1.0.3

2 release files

1.0.2

2 release files

1.0.1

2 release files

1.0.0

2 release files

0.9.13

2 release files

0.9.10

2 release files

0.9.9

2 release files

0.9.8

2 release files

0.9.7

2 release files

0.9.6

2 release files

0.9.5

2 release files

0.9.4

2 release files

0.9.3

2 release files

0.9.2

2 release files

0.9.1

2 release files

0.9.0

2 release files

0.8.5

2 release files

0.8.4

2 release files

0.8.3

2 release files

0.8.2

2 release files

0.8.1

2 release files

0.8.0

2 release files

0.7.8

2 release files

0.7.7

2 release files

0.7.6

2 release files

0.7.5

2 release files

0.7.4

2 release files

0.7.3

2 release files

0.7.2

2 release files

0.7.1

2 release files

0.7.0

2 release files

0.6.2

2 release files

0.6.1

2 release files

0.6.0

2 release files

0.5.15

2 release files

0.5.14

2 release files

0.5.13

2 release files

0.5.12

2 release files

0.5.11

2 release files

0.5.10

2 release files

0.5.9

2 release files

0.5.8

2 release files

0.5.7

2 release files

0.5.6

2 release files

0.5.5

2 release files

0.5.4

2 release files

0.5.3

2 release files

0.5.2

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.4.9

2 release files

0.4.8

2 release files

0.4.7

2 release files

0.4.6

2 release files

0.4.5

2 release files

0.4.4

2 release files

0.4.3

2 release files

0.4.2

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.7

2 release files

0.2.6

2 release files

0.2.5

2 release files

0.2.4

2 release files

0.2.3

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.6

2 release files

0.1.5

2 release files

0.1.4

2 release files

0.1.1

2 release files

0.1.0

2 release files

0.0.5

2 release files

0.0.4

2 release files

0.0.3

2 release files

0.0.1

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