Sharing a root-cause finding from an investigation on 2026-04-24. Not a fix PR yet. Posting because this appears to unify at least 10 open issues across pytoyoda/pytoyoda and pytoyoda/ha_toyota, including the predecessor DurgNomis-drol repos.
GET /v1/global/remote/status is a cache read. The cache is populated when the car's cellular modem transmits. That only happens during active trips (assumed auto-report from the car, to be validated), or when someone issues POST /v1/global/remote/refresh-status to command the car to wake up and transmit, for example whenever a user opens the Toyota Android app and the app requests a fresh read. pytoyoda only implements the GET side; the official Toyota app uses both.
So the unification: it is not that different endpoints have different problems. It is that /v1/global/remote/status is a single flaky endpoint that happens to be the ONLY source of door/lock/window/hood state, and the entire integration goes unavailable when it fails to respond. Two orthogonal issues come out of this one endpoint:
- HTTP 429
APIGW-403 "Unauthorized": the endpoint is just flaky. Requests to it randomly fail. Retries often succeed a few seconds later. This is independent of cache state. - Door/window/lock/hood sensors stuck on last-trip values: even when the endpoint does respond, what it returns is a stale cache. Without an explicit POST, the cache is whatever the car last auto-reported, which was during the previous drive. When the car is parked, nothing repopulates the cache.
The irony, and the reason this has been hard to diagnose for four years: the "flaky API" everyone has been working around is the one endpoint that, even when it works, returns data we could just as well have cached client-side. Nobody ever looked past the 429 rate because it read like a rate-limit problem; nobody noticed that the 200 responses were already stale.
After disabling the integration for 2h to let the account cool off, ran a controlled test via a small harness calling pytoyoda.Api directly:
22:03:12 CEST GET /v1/global/remote/status -> 429 APIGW-403 "Unauthorized"
22:03:12 CEST POST /v1/global/remote/refresh-status -> 200 ONE-GLOBAL-RS-10000 "Request Completed Successfully"
(body: {deviceId, deviceType:"Android", guid, vin})
(wait 30 seconds for the car to transmit)
22:03:43 CEST GET /v1/global/remote/status -> 200 occurrence_date=20:03:27Z
(occurrence_date is 15s after the POST: that is the car waking up, transmitting, and the backend ingesting the payload)
22:04:43 CEST GET /v1/global/remote/status (+60s) -> 200 occurrence_date=20:03:27Z (unchanged)
Later, an 18-minute-idle probe on the same VIN returned 4 x 200 out of 10 rapid attempts, all four 200s carrying the same occurrence_date from the earlier POST. The endpoint stayed flaky whether calls were 1 second apart or 6 minutes apart. When it did respond, the data it returned never advanced because nothing refreshed the cache in the meantime.
Two distinct response prefixes show where the responses come from:
- Failure:
"responseCode": "APIGW-403"- Toyota's API Gateway layer - Success:
"responseCode": "ONE-GLOBAL-RS-10000"- Toyota's backend application layer
Why the endpoint is sometimes unavailable and sometimes not, I cannot say from this data. It's flaky. Mechanism unknown.
Several people came close without connecting everything:
- DurgNomis-drol/mytoyota PR #302 by @GitOldGrumpy (2024, unmerged): tested all three
/v1/global/remote/refresh-*endpoints and implementedVehicle.force_update(). Never merged because mytoyota was discontinued. This is the closest earlier attempt. - DurgNomis-drol/mytoyota #239 by @iksuli (2023): documented using
force_update()to refresh EV battery data. - pytoyoda/pytoyoda PR #77 (merged 2025): ported the EV-specific sibling
/v1/global/remote/electric/realtime-status. Good template for arefresh_vehicle_status()method addition modeled on the same pattern. - ha_toyota#157 by @julesxxl and @aubreyz (2025): verbatim "If I refresh the doors status in the MyToyota app and then reload the integration, the doors status is updated." @aubreyz measured app latency at 4-8 min vs HA never updating. Closed to discussion without resolution.
- ha_toyota#137 by @jbbandos: "got solved automatically as soon as I opened the app and refreshed the status. Seems like my car had not communicated with Toyota's servers for over 12h."
Multiple people observed that opening the app fixes HA. None named the POST /refresh-status step that the app does behind the scenes.
Current repos:
- pytoyoda/ha_toyota#87 - 6+ users across Yaris/Corolla/Highlander/Yaris Cross with doors/hood/trunk permanently stuck "open"
- pytoyoda/ha_toyota#120 - @alewew mentions NA version had a refresh service
- pytoyoda/ha_toyota#137 - various "unknown" / stuck data, resolved only by opening the app
- pytoyoda/ha_toyota#150 - explicit feature request for the (electric) refresh-status button
- pytoyoda/ha_toyota#157 - polling-layer documentation, MyToyota-app refreshes trigger HA updates
- pytoyoda/ha_toyota#190 - remaining charge time doesn't update
- pytoyoda/ha_toyota#229 - Lexus 12V battery drain from auth errors on the realtime-status POST. That specific case was EV-related, but the design concern is the same for us:
POST /refresh-statuscan wake the car and is a potential source of 12V drain if called too eagerly. - pytoyoda/ha_toyota#281 - "Many disconnections with Unauthorized" - the 429 rate issue
- pytoyoda/ha_toyota#284 - "Lock/Door states never update until new trip" - the report that clarified the door-staleness half of the pattern
- pytoyoda/pytoyoda#161 - @MHarlock reports the exact
APIGW-403 Unauthorizedbody two days ago, currently misclassified as a login bug
Predecessor repos:
- DurgNomis-drol/ha_toyota#241, #244, #259 - same door/lock staleness symptoms, maintainer attributed to Toyota endpoint changes
Decompiled from com.toyota.oneapp.eu 2.8.1:
public final class RefreshVehicleStatusRequest {
@SerializedName("deviceId") String deviceId;
@SerializedName("deviceType") String deviceType = "Android";
@SerializedName("guid") String guid;
@SerializedName("vin") String vin;
}Retrofit interface declaration:
@Headers({"Content-Type: application/json"})
@POST("/v1/global/remote/refresh-status")
Object v(@Body RefreshVehicleStatusRequest body, Continuation<? super BaseCommandResponse> continuation);In my test I used the same UUID pytoyoda already generates (controller._uuid, already sent as the x-guid header) for both deviceId and guid. No device-registration precheck; POST accepted on first try.
Two changes, both small:
pytoyoda: add VEHICLE_GLOBAL_REMOTE_REFRESH_STATUS_ENDPOINT + refresh_vehicle_status(vin) method in api.py + a wrapper on Vehicle. Modeled on PR #77's electric sibling.
ha_toyota: smart per-cycle strategy that minimises cellular wake-ups, since #229 already shows that aggressive wake-up calls can drain the car's 12V battery. The details:
- Each cycle, GET
/v3/telemetryfirst (cheap, reliable). Derivecar_movingfrom odometer delta. - Maintain a client-side cache of the last successful
/v1/global/remote/statusresponse plus itsoccurrence_date, so subsequent cycles can reuse it without refetching an unchanged server cache. - Only
POST /refresh-statuswhen:- Car just stopped (odometer changed in the previous cycle but not this one) - to catch door-closing events as the user exits
- User explicitly calls a new HA service
toyota.refresh_vehicle_status(to be added) - An opt-in option
auto_wake_if_idleis ON andlast_post > 12h ago. Default OFF, battery-safe.
- Only
GET /v1/global/remote/statuswhen:- Car is moving (cache is being populated by auto-reports)
- Car just stopped, or we just POSTed, or our cached
occurrence_dateis older than some threshold - Otherwise: serve
LockStatusfrom the client-side cache. Repeatedly fetching an unchanged server-side cache is wasted quota.
For a daily-driven car parked ~23h/day, this cuts /v1/global/remote/status traffic from ~240 GETs/day to ~10-15, with 0-2 POSTs. Respects Toyota's infrastructure, avoids unnecessary modem wake-ups, and keeps door/lock state fresh during and just after drives, which is when it actually changes.
- That the car auto-reports lock state changes during active trips. Reasonable assumption since that is when the modem is hot, but not tested by me.
- Exact cache TTL. I observed it unchanged long enough to be confident it holds for at least tens of minutes, probably longer.
- Whether there is a quota on the POST
/refresh-statusitself. My tests fired only one POST and it succeeded; sustained use might hit a limit.
- No alternative endpoint for door/lock/window/hood state exists in the EU API surface. I checked
/v1/vehiclehealth/status(warning lights only),/v3/telemetry(fuel/odometer/HV-battery only),/v2/notification/history(opaque messages, not state), and scanned all APK-declared paths./v1/global/remote/statusis the sole source ofLockStatusdata, so we can't route around the cache mechanism by reading a different endpoint. - No 12V / auxiliary-battery telemetry endpoint is exposed. Toyota surfaces only EV traction-battery SoC. We therefore cannot build a reactive "don't POST if 12V is low" safeguard; the battery cost of any POST is invisible to us at call time. That is the reason the design prefers architectural minimisation (POST only when meaningfully beneficial) over adaptive throttling.
Build the two changes in the coming days and soak on live HA for a day or two, then open PRs. Happy to collaborate with maintainers or anyone else who wants to help validate.
Not trying to step on anyone's toes. Genuine thanks to @CM000n for keeping these libraries alive, and to everyone whose issue comments added pieces of this puzzle. The "Toyota API is flaky" framing has been the working assumption for years, and the API is indeed flaky, but the irony is that the one endpoint that flakes is also the one that, even when it responds, only serves a stale cache unless someone wakes the car. Flagging it as cleanly as I can so whoever builds the fix can start from the right model.