# How Does the TeslaMate API Module Handle Rate Limiting and Unauthorized Errors?

> Discover how the TeslaMate API module manages rate limiting with retry headers and unauthorized errors via circuit-breaker pattern. Prevent repeated auth failures.

- Repository: [TeslaMate/teslamate](https://github.com/teslamate-org/teslamate)
- Tags: internals
- Published: 2026-06-23

---

**The TeslaMate API module handles rate limiting by returning structured error tuples with retry-after headers, while unauthorized errors trigger a circuit-breaker pattern using the `:fuse` library to prevent repeated authentication attempts.**

The `TeslaMate.Api` module in the `teslamate-org/teslamate` repository serves as the primary interface to the official Tesla API, encapsulating all HTTP interactions and providing resilient error handling for two critical failure modes: rate limiting (`429 Too Many Requests`) and unauthorized access (`401 Unauthorized`).

## Circuit Breaker Architecture for Unauthorized Errors

When the Tesla API returns an unauthorized response, the module does not immediately retry the request or refresh the token blindly. Instead, it implements a **circuit breaker** using the Erlang `:fuse` library to protect against cascading failures from repeated bad authentication attempts.

### Fuse Configuration in [`lib/teslamate/api.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/teslamate/api.ex)

During GenServer initialization, the `init/1` function installs a fuse specific to the API instance using a standard policy of **5 failures** within **10 minutes**. If the threshold is exceeded, the fuse remains blown for approximately 9,999 hours until manually reset.

```elixir

# In lib/teslamate/api.ex

:fuse.install(
  :"#{name}.unauthorized",
  {{:standard, 5, :timer.minutes(10)}, {:reset, :timer.hours(9999)}}
)

```

This configuration ensures that after five consecutive authentication failures, the system stops attempting to contact the Tesla API and treats subsequent requests as signed out.

### Unauthorized Error Processing in `handle_result/3`

The `handle_result/3` function pattern matches on `%TeslaApi.Error{reason: :unauthorized}` and executes a three-step recovery process:

1. **Melt the fuse** – Increment the failure counter via `:fuse.melt/1`
2. **Query fuse status** – Check if the circuit is `:blown` or still `:ok`
3. **Conditional response** – Either delete the stored auth token or trigger a token refresh

```elixir

# From lib/teslamate/api.ex (handle_result/3)

{:error, %TeslaApi.Error{reason: :unauthorized}} ->
  :ok = :fuse.melt(:"#{name}.unauthorized")

  case :fuse.ask(:"#{name}.unauthorized", :sync) do
    :blown ->
      # Too many failures: sign out completely

      true = :ets.delete(name, :auth)
      {:error, :not_signed_in}

    :ok ->
      # Still within threshold: attempt token refresh

      send(name, :refresh_auth)
      {:error, :unauthorized}
  end

```

If the fuse is blown, the module clears the ETS table entry storing the authentication credentials, forcing the user to re-authenticate. If intact, it dispatches a `:refresh_auth` message to asynchronously renew the access token via `TeslaApi.Auth.refresh/1`.

## Rate Limit Detection and Response

When the Tesla API enforces rate limiting, it returns a `429` status code with a `retry-after` header. The `handle_result/3` function extracts this value and returns a three-tuple error containing the specific atoms `:too_many_request` and the numeric retry duration.

```elixir

# From lib/teslamate/api.ex (handle_result/3)

{:error, %TeslaApi.Error{reason: :too_many_request, message: retry_after}} ->
  Logger.warning("TeslaApi.Error / :too_many_request #{retry_after}")
  {:error, :too_many_request, retry_after}

```

This structured response allows calling processes to implement intelligent backoff strategies, sleeping for the exact duration specified by the API before retrying.

## Practical Error Handling Examples

### Handling Rate Limits in Application Code

When calling `TeslaMate.Api.list_vehicles/0` or similar functions, pattern match on the error tuples to implement appropriate retry logic:

```elixir
case TeslaMate.Api.list_vehicles() do
  {:ok, vehicles} ->
    process_vehicles(vehicles)

  {:error, :too_many_request, retry_after} ->
    :timer.sleep(retry_after * 1000)
    retry_request()

  {:error, :unauthorized} ->
    IO.puts("Authentication expired, attempting refresh...")

  {:error, :not_signed_in} ->
    IO.puts("Please authenticate to continue")
end

```

### Monitoring Fuse State for Debugging

You can inspect the circuit breaker state directly using the `:fuse` module to determine if the API is currently rejecting requests due to repeated authentication failures:

```elixir
case :fuse.ask(:"TeslaMate.Api.unauthorized", :sync) do
  :ok -> IO.puts("Circuit closed: API calls allowed")
  :blown -> IO.puts("Circuit open: Authentication locked out")
end

```

## Key Implementation Files

- **[`lib/teslamate/api.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/teslamate/api.ex)** – Contains the `handle_result/3` function and fuse management logic for rate limiting and unauthorized error handling
- **[`lib/tesla_api/error.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/tesla_api/error.ex)** – Defines the `%TeslaApi.Error{}` struct used for pattern matching error reasons
- **[`lib/tesla_api/auth.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/tesla_api/auth.ex)** – Implements `Auth.refresh/1` invoked when the circuit breaker permits token renewal
- **[`test/teslamate/api_test.exs`](https://github.com/teslamate-org/teslamate/blob/main/test/teslamate/api_test.exs)** – Test suite validating the unauthorized and rate-limiting error paths

## Summary

- **Unauthorized errors** trigger a circuit breaker in `handle_result/3` that melts after repeated failures and either signs the user out (if blown) or initiates token refresh
- **Rate limiting** returns `{:error, :too_many_request, retry_after}` with the exact seconds to wait, enabling precise backoff implementation
- The **`:fuse` library** provides the circuit breaker mechanism with a 5-failure threshold over 10 minutes, installed during `init/1`
- All error handling logic resides in [`lib/teslamate/api.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/teslamate/api.ex), leveraging ETS for credential storage and GenServer messaging for asynchronous token refresh

## Frequently Asked Questions

### How does TeslaMate prevent infinite authentication loops when the Tesla API returns 401 errors?

The `TeslaMate.Api` module uses the `:fuse` library to create a circuit breaker that melts on each unauthorized response. After five melts within ten minutes, the fuse blows, causing the module to delete the stored authentication data and return `{:error, :not_signed_in}` rather than continuing to retry with invalid credentials.

### What should I do when receiving a `{:error, :too_many_request, retry_after}` response?

Extract the `retry_after` value from the error tuple and implement a delay before retrying your request. The integer represents seconds, so multiply by 1000 when using `:timer.sleep/1` or schedule a Process send_after for asynchronous handling.

### Where is the authentication token actually refreshed when an unauthorized error occurs?

When the fuse is still intact (less than five recent failures), `handle_result/3` sends a `:refresh_auth` message to the GenServer, which triggers `TeslaApi.Auth.refresh/1` in [`lib/tesla_api/auth.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/tesla_api/auth.ex) to obtain a new access token using the stored refresh token.

### Can I manually reset the circuit breaker if I fix authentication issues externally?

Yes, since the fuse is registered with the name `:"TeslaMate.Api.unauthorized"`, you can call `:fuse.reset(:"TeslaMate.Api.unauthorized")` from an IEx session or administrative function to immediately close the circuit and allow API requests to resume.