Teslita API v1

Teslita API

The Teslita API gives third-party applications read access to a Teslita user's vehicle, trip, charging, and pack-health data, plus a narrow set of write endpoints for triggering server-side work like a supercharger history sync.

The API is organised around REST, uses predictable resource-oriented URLs, accepts and returns JSON, and authenticates requests with bearer tokens. All requests run over TLS — plain HTTP is rejected.

Base URL

https://api.teslita.com

How keys work

Every API key belongs to a single Teslita user, and every request it authenticates resolves to that user's data. There is no multi-tenant key: a partner that needs access to more than one Teslita account needs one key per account. Keys are minted by the account owner and shown once at creation time — only a SHA-256 hash is stored server-side, so a lost key cannot be recovered and must be replaced.

Authentication #

Authenticate requests by sending your key in the Authorization header as a bearer token. Keys look like tsk_ followed by 40 URL-safe characters (~230 bits of entropy). Never commit them to source control, log them, or send them to the browser — treat them the way you'd treat a database password.

Scopes

Three scopes are defined. A key may carry any combination.

read All GET endpoints: vehicles, trips, charging, pack health, settings.
write Server-side data operations — for example, triggering a supercharger history sync. Does not include physical vehicle commands.
control Physical commands sent to the vehicle: start / stop charging today, more to come. Separate from write so operators can grant data sync without granting car control.

Calling an endpoint with a key that lacks the required scope returns 403 forbidden.

Failures

A missing, malformed, or revoked key returns 401. We do not distinguish between "not sent" and "sent but invalid" in the error message to avoid helping credential stuffing.

Conventions #

Timestamps

Every timestamp is ISO-8601, always UTC, always suffixed Z. Example: 2026-04-24T17:30:16Z.

Identifiers

Integer database IDs are serialised as strings in JSON to sidestep the 53-bit integer limit in JavaScript. Treat them as opaque — don't do arithmetic with them. VINs (17 uppercase characters) are the business key for vehicles throughout the API.

Units

Distance in kilometres, energy in kWh, power in kW, voltage in V, tyre pressure in bar, temperatures in °C. Costs are returned in the session's native currency; partners are expected to convert themselves if needed. Units are baked into field names where it could be ambiguous (_kwh, _kmh, _bar, _c).

Pagination

List endpoints paginate cursor-style. Pass ?limit= (default 50, max 200) and ?cursor=. The response carries next_cursor — pass it back as cursor to fetch the next page. When next_cursor is null, you've reached the end. Cursors are opaque; do not parse them.

Date filters

?from= and ?to= accept either ISO-8601 (2026-04-24T00:00:00Z) or the shorter YYYY-MM-DD. Both bounds are inclusive and interpreted as UTC.

Errors #

Errors are returned as JSON with a stable shape so you can parse them without switching on HTTP status alone. The code is machine-readable and stable; the message is human-readable and may change.

missing_auth401 Unauthorized No Authorization header was sent.
invalid_auth401 Unauthorized Key is unknown, malformed, or revoked.
forbidden403 Forbidden Key lacks a required scope (e.g. write).
not_found404 Not Found Route doesn't exist, or resource isn't owned by this user.
bad_request400 Bad Request A parameter is missing, malformed, or out of range.
rate_limited429 Too Many Requests Per-key cap exceeded. Back off and retry after 60 seconds.
tesla_not_connected409 Conflict User has no linked Tesla account — required for writes that proxy Tesla.
upstream_error502 Bad Gateway A required upstream service (typically Tesla) failed.
internal_error500 Internal Error Unexpected server-side failure. Retrying may help; if it persists, contact support.

Rate limits #

Every key has a fixed-window rate limit, measured per 60-second window. The default is 120 requests per minute. Operators can set a per-key override at mint time if a partner has a legitimate need for a higher ceiling.

Exceeding the limit returns 429 rate_limited. The cap resets at the start of the next window. We recommend exponential backoff with jitter for retries; a tight retry loop will simply bounce off the same limit.

Rate limit counters are shared per-key, not per-IP. If you distribute calls across multiple servers using the same key, they all count toward the same bucket.

Versioning #

Every route carries the API version in the URL (/v1/…). Within a version, we only make additive changes: we may add new fields, new endpoints, or new optional parameters, but we will not remove existing fields, rename them, or change their types.

Breaking changes ship as a new version prefix (/v2/…). Older versions are supported in parallel for a documented deprecation window before retirement.

Treat unknown fields as optional — if you see a field you haven't seen before in a response, you can safely ignore it without breaking.

GET /v1/health public

Health check #

Liveness probe for uptime monitoring. Returns quickly, does not require authentication, and does not hit the database. The response shape is stable — monitoring tools can key on status === "ok".

GET /v1/me read

Retrieve the current user #

Returns the Teslita user your key belongs to, plus metadata about the key itself. Useful for the very first request — it confirms the key is valid, shows which account and scopes it carries, and lets you detect a stale or swapped-out key.

Returns

user.idstring The Teslita user's ID. Opaque — use for equality checks only.
user.emailstring Account email.
user.display_namestring | null Name the user set, if any.
user.languagestring | null Two-letter language code (en, de, …).
key.scopesarray<string> Scopes carried by this key. One or both of read, write.
GET /v1/settings read

Retrieve user settings #

The user's display preferences, home location, and locale configuration. This is what drives how the Teslita UI itself renders numbers and dates — mirroring it in a partner app keeps experiences consistent.

Returns

languagestring | nullTwo-letter code.
pressure_unitstring | nullbar or psi.
temperature_unitstring | nullcelsius or fahrenheit.
countrystring | nullISO 3166-1 alpha-2 code.
currencystring | nullISO 4217 code (EUR, USD, …).
timezonestring | nullIANA zone name.
home.latitudenumber | nullSigned decimal degrees.
home.longitudenumber | nullSigned decimal degrees.
home_kwh_pricenumber | nullUnit price used to estimate cost of home-charging sessions.
GET /v1/vehicles read

List vehicles #

Every vehicle linked to the user's Tesla account. Includes archived vehicles the user no longer owns — these have disconnected_at set. Their historical trip and charging data is preserved and still queryable.

Returns

data[].vinstring17-character Tesla VIN. Use as the path parameter on vehicle-scoped endpoints.
data[].display_namestring | nullName set in the Tesla app.
data[].custom_namestring | nullNickname set inside Teslita (overrides display_name in UIs).
data[].modelstring | nullModel S, Model 3, Model X, Model Y, Cybertruck, Roadster, Semi.
data[].telemetry_enabledbooleanWhether Fleet Telemetry is provisioned on the vehicle.
data[].disconnected_attimestamp | nullNon-null if Tesla no longer returns this VIN on the user's fleet.
GET /v1/vehicles/{vin} read

Retrieve a vehicle #

Same shape as a single entry in list vehicles, wrapped in a data envelope. Returns 404 not_found if the VIN is not owned by this user.

Path parameters

vinstringrequired 17-character Tesla VIN from list vehicles.
GET /v1/vehicles/{vin}/state read

Live state #

Latest telemetry snapshot for a vehicle, sourced from Tesla Fleet Telemetry and stored by the Teslita consumer daemon. Expect updates every few seconds while the vehicle is driving or charging, and sparse updates while parked.

If no telemetry has been received yet for this vehicle (e.g. telemetry provisioning is still pending), data is null and a human-readable reason is included.

Returns

last_signal_attimestampWhen Tesla last sent us anything for this vehicle.
charging_statestring | nullIdle, Charging, Complete, Stopped, …
battery_levelinteger | nullState of charge, 0–100.
charge_limit_socinteger | nullUser-set charge limit, 0–100.
energy_remaining_kwhnumber | nullBest available estimate of usable battery energy.
odometer_kmnumber | nullFull odometer reading.
location.latitudenumber | nullSigned decimal degrees.
location.longitudenumber | nullSigned decimal degrees.
gearstring | nullShiftStateP, ShiftStateD, ShiftStateR, ShiftStateN.
vehicle_speed_kmhnumber | nullInstantaneous speed in km/h.
GET /v1/vehicles/{vin}/at-home read

Is the car at home? #

A yes/no derived by haversine distance between the vehicle's last-known position (from Fleet Telemetry) and the user's configured home coordinates (from settings). No Tesla call, no wake-up. Under the default per-key rate limit with no per-endpoint cap.

The threshold is the user's home_radius_m, or 150 m by default when none is configured — enough to cover a driveway plus GPS jitter without catching the neighbour. distance_m is always included so you can apply your own tolerance if 150 m doesn't fit.

If the user hasn't set a home location at all, data is null and reason explains why. Similarly if no telemetry has been received for the vehicle.

Returns

at_homebooleantrue when distance_m ≤ home_location.radius_m.
distance_mintegerGreat-circle distance in metres between vehicle and home, rounded to the nearest metre.
vehicle_locationobjectlatitude and longitude from the last telemetry signal.
home_locationobjectConfigured latitude, longitude, radius_m, and radius_source ("configured" or "default").
is_freshbooleantrue when the telemetry position is within 5 minutes old. A stale true may mean the car is parked at home and sleeping (which is normal).
GET /v1/vehicles/{vin}/pack-health read

Latest pack health #

Most recent pack-health sample for a vehicle: brick balance, module temperatures, isolation resistance, drive-unit states, and tyre pressures. Sampled hourly when the vehicle is reporting.

Returns (selected)

battery.imbalance_mvinteger | nullMax − min brick voltage, in millivolts. A quick proxy for cell balance — under 30 mV is healthy.
battery.module_temp_max_cnumber | nullHottest module temperature at sampling time.
battery.isolation_resistance_ohminteger | nullHV isolation, ohms. Dropping values can signal insulation degradation.
drive_unitsobjectPer-drive-unit inverter state: front, rear, rear_left, rear_right. Nulls indicate absent units.
tpms_barobjectTyre pressure in bar, one field per corner.
GET /v1/vehicles/{vin}/pack-health/samples read

Pack-health time series #

Historical pack-health samples ordered oldest-first within the range. Defaults return up to 500 rows; the hard cap is 2000. Filter with from/to for longer windows.

Query parameters

fromstringoptionalISO-8601 or YYYY-MM-DD. Inclusive lower bound on sampled_at.
tostringoptionalISO-8601 or YYYY-MM-DD. Inclusive upper bound on sampled_at.
limitintegeroptionalDefault 500, max 2000.
GET /v1/trips read

List trips #

Completed trips, newest first, cursor-paginated. Active (in-progress) trips are not included here — they show up on vehicle state while they're running, and appear here once they end.

Query parameters

vinstringoptionalRestrict to one vehicle.
fromstringoptionalISO-8601 or YYYY-MM-DD. Inclusive lower bound on started_at.
tostringoptionalISO-8601 or YYYY-MM-DD. Inclusive upper bound.
limitintegeroptionalDefault 50, max 200.
cursorstringoptionalValue of next_cursor from the previous response.

Returns (selected)

data[].idstringOpaque trip identifier. Use in retrieve a trip and trip route.
data[].started_attimestampWhen the vehicle shifted out of Park.
data[].ended_attimestampWhen the vehicle returned to Park and stayed there.
data[].distance_kmnumberPath distance, not straight-line.
data[].energy_used_kwhnumber | nullDerived from start/end EnergyRemaining snapshots; null when inputs are unreliable.
data[].efficiency_wh_per_kminteger | nullComputed only when both energy and distance are usable.
next_cursorstring | nullPass back as cursor for the next page. null means you've reached the end.
GET /v1/trips/{id} read

Retrieve a trip #

Returns a single trip. Same fields as an entry from list trips, wrapped in a data envelope. A trip owned by a different user, or an unknown id, returns 404 not_found.

GET /v1/trips/{id}/route read

Trip route #

GPS waypoints for a single trip, ordered by recorded_at ascending. These are the samples Teslita collects from Fleet Telemetry and stitches together to form the path. Any individual field on a waypoint can be null — the vehicle doesn't publish every signal on every tick.

Point density varies with speed and signal type. Expect sub-second spacing while moving and tens of seconds while idling at a light. Downsample client-side if you need a fixed cadence.

GET /v1/charging/sessions read

List charging sessions #

Charging sessions from all sources — home, destination, and Supercharger — merged into one timeline, newest first, cursor-paginated. Each session's type classifies where it happened.

Query parameters

vinstringoptionalRestrict to one vehicle.
typestringoptionalOne of home, supercharger, other.
fromstringoptionalInclusive lower bound on started_at.
tostringoptionalInclusive upper bound on started_at.
limitintegeroptionalDefault 50, max 200.
cursorstringoptionalFrom previous next_cursor.

Returns (selected)

data[].session_idstringTesla's id for supercharger sessions, or a synthetic tlm_* id for telemetry-reconstructed home/destination sessions.
data[].energy_added_kwhnumber | nullBattery-side energy added.
data[].energy_grid_kwhnumber | nullGrid-side energy pulled (available on home/destination where a meter is present).
data[].costobjectNative amount plus ISO currency. amount is null when Tesla hasn't invoiced and no home-price estimate is configured.
data[].invoice_availablebooleantrue if a Tesla-issued PDF can be fetched via invoice PDF.
GET /v1/charging/sessions/{session_id} read

Retrieve a charging session #

A single session. Shape matches an entry from list sessions, wrapped in a data envelope. session_id values from Tesla can contain characters that need URL-encoding — encode them before interpolating into the path.

GET /v1/charging/sessions/{session_id}/samples read

Charging session time series #

Power, battery level, and energy-added over the life of a charging session. Samples come from the telemetry consumer daemon and are ordered oldest-first, making the response directly consumable as chart input.

Supercharger sessions imported from Tesla's history endpoint don't carry per-sample telemetry; this endpoint returns an empty samples array for those.

GET /v1/charging/invoices read

List invoices #

Supercharger sessions that have a Tesla-issued PDF invoice available. Only these sessions have retrievable PDFs — home sessions are Teslita-estimated, not Tesla-invoiced. Each entry carries a pdf_url pointing at the binary download endpoint.

Query parameters

vinstringoptionalRestrict to one vehicle.
limitintegeroptionalDefault 50, max 200.
cursorstringoptionalFrom previous next_cursor.
GET /v1/charging/invoices/{session_id}.pdf read

Download invoice PDF #

Streams the Tesla-issued Supercharger invoice PDF as binary application/pdf. The first fetch may round-trip to Tesla to retrieve the PDF; subsequent fetches are served from a local gzip cache in under 50ms.

Every call appends a row to the invoice download audit log so the account owner can see who downloaded what. This applies to API-key downloads and UI downloads equally.

If the session has no Tesla invoice on file, the response is 404 not_found.

GET /v1/vehicles/{vin}/charging/status read

Live charging status #

A focused view of what the vehicle is doing right now: charging state, SoC, power, projected time-to-full. Backed by the local Fleet Telemetry cache — this endpoint never calls Tesla and never wakes the car, so it's safe to poll regularly.

Under the default per-key rate limit (120/min), with no per-endpoint cap. If the car is asleep and therefore not publishing telemetry, is_fresh becomes false and data_age_seconds tells you how stale the view is.

Returns

charging_statestring | nullCharging, Idle, Starting, Stopped, Complete, Disconnected, NoPower.
battery_levelinteger | nullState of charge, 0–100.
charge_limit_socinteger | nullUser-set charge limit, 0–100.
energy_added_kwhnumber | nullEnergy added during the current session (resets on plug-in).
charger_power_kwnumber | nullInstantaneous charging power.
charger_voltagenumber | nullCharger voltage. Low values (≈1 V) are Tesla's "not connected" sentinel, not a real reading.
time_to_full_mininteger | nullEstimated minutes to reach charge_limit_soc, from the car's own projection.
fast_charger.presentboolean | nullWhether a DC fast charger is currently connected.
data_age_secondsinteger | nullSeconds since the car last sent us telemetry.
is_freshbooleantrue when data_age_seconds is within 5 minutes. Used internally by start/stop to decide whether to short-circuit.
GET /v1/vehicles/{vin}/charging/plugged-in read

Is the car plugged in? #

A dedicated yes/no for "is the charge cable connected to the car right now?". Computed from the same Fleet Telemetry cache as charging status — no Tesla call, no wake-up, safe to poll. Under the default per-key rate limit with no per-endpoint cap.

Under the hood we check both charging_state and detailed_charge_state for any variant of Disconnected, because Tesla sometimes updates one slightly before the other during a state transition. plugged_in is null only when both state fields are missing (no telemetry yet).

Returns

plugged_inboolean | nulltrue when the car is physically connected to a charger (including idle / stopped / complete states while plugged in). null when no telemetry has been received.
at_homeboolean | nullWhether the car is currently inside its configured home geofence — useful for distinguishing "plugged in at home" from "plugged in at a public charger". null when the home location isn't configured or the vehicle hasn't reported a position. See at-home for distance + coordinates.
charging_statestring | nullSame value as in charging status. Surfaced so callers can distinguish plugged-but-idle from plugged-and-charging.
detailed_charge_statestring | nullTesla's more granular enum variant.
is_freshbooleantrue when data_age_seconds ≤ 300.
POST /v1/vehicles/{vin}/charging/sync write

Sync supercharger sessions #

Triggers an incremental Supercharger-history sync for one vehicle. Teslita asks Tesla for any sessions not already in its database and persists them. The operation is idempotent: running it repeatedly against a quiet account returns inserted: 0, updated: 0.

Rate limit: 24 requests per vehicle per 24h. This sits alongside (and below) the global 120-per-minute per-key cap — every call proxies Tesla's fleet API, which is expensive upstream and itself rate-limited. The counter is keyed on the user + VIN, not the API key, so rotating keys doesn't reset it. Averaged out, 24/day is roughly one sync per hour — well above any realistic integration cadence.

Home and destination charging are assembled live from Fleet Telemetry and do not flow through this endpoint. Only supercharger sessions go through here.

Path parameters

vinstringrequired 17-character Tesla VIN.

Returns

totalintegerTotal number of sessions Tesla returned (including ones we already had).
insertedintegerSessions newly persisted.
updatedintegerExisting sessions whose fields changed (e.g. late invoice arrival).

Errors

forbidden403Key lacks the write scope.
tesla_not_connected409User has no linked Tesla account.
upstream_error502Tesla's history endpoint failed.
POST /v1/vehicles/{vin}/charging/start control

Start charging #

Asks the vehicle to start charging. Requires that the car is plugged in and not already charging. Tesla will wake the car automatically if it is asleep — expect the call to take up to ~60 seconds in that case.

How the short-circuit works

Before hitting Tesla's API, we consult the local Fleet Telemetry cache. If data_age_seconds ≤ 300 and the cached charging_state already shows Charging or Starting, the endpoint returns immediately with result: "already_charging"no Tesla call, no wake-up, no rate-limit consumption. If the cached state shows Disconnected, we fail fast with 409 not_plugged_in for the same reasons.

Only when the telemetry is stale or the state is ambiguous do we fall through to a signed Tesla command. The source field in the response tells you which path was taken.

Rate limit: 24 commands per vehicle per 24h, shared with charging/stop. Only calls that actually reach Tesla consume a slot — short-circuited calls are free. Keyed on user_id + vin so rotating API keys does not reset the counter.

Path parameters

vinstringrequired 17-character Tesla VIN.

Returns

commandstringAlways "start".
resultstring"issued" when Tesla accepted the command, or "already_charging" when we short-circuited.
sourcestring"local_telemetry" or "tesla_api".
stateobjectPresent when source === "local_telemetry": the cached charging status as a convenience for callers.

Errors

forbidden403Key lacks the control scope.
not_plugged_in409Fresh telemetry shows charging_state: "Disconnected".
tesla_not_connected409User has no linked Tesla account.
rate_limited42924-per-day shared start/stop budget exceeded.
command_failed502Tesla rejected the command for a reason we didn't translate to success (e.g. charge port latch failure).
upstream_error502Tesla's API was unreachable or returned HTTP 5xx.
POST /v1/vehicles/{vin}/charging/stop control

Stop charging #

Asks the vehicle to stop charging. Mirror of charging/start — same short-circuit strategy, same 24-per-day budget, same return shape.

If fresh local telemetry shows the car is not currently charging (any state other than Charging or Starting), the endpoint returns already_stopped without calling Tesla. Otherwise it sends a signed charge_stop command.

Rate limit: 24 commands per vehicle per 24h, shared with charging/start. Start and stop pull from the same bucket — 24 Tesla calls per vehicle per day total, not 24 of each.

Returns

commandstringAlways "stop".
resultstring"issued" or "already_stopped".
sourcestring"local_telemetry" or "tesla_api".