# How TeslaMate Handles API Rate Limits: Detection, Retry Logic, and Back-Off Strategy

> Discover how TeslaMate expertly manages Tesla API rate limits. Learn about its detection, retry logic, and back-off strategy to avoid spamming the API.

- Repository: [TeslaMate/teslamate](https://github.com/teslamate-org/teslamate)
- Tags: how-to-guide
- Published: 2026-06-18

---

**TeslaMate detects HTTP 429 responses from the Tesla Owner API, extracts the `Retry-After` header value (defaulting to 300 seconds), and propagates a structured error tuple that allows the application to back off gracefully without spamming the API.**

When ingesting data from the Tesla Owner API, TeslaMate must respect global rate limits to maintain reliable access. According to the teslamate-org/teslamate source code, the application implements a robust strategy to detect HTTP 429 "Too Many Requests" responses, parse retry delays, and propagate structured errors through the Elixir call stack.

## How TeslaMate Detects Rate Limit Responses

The detection logic resides in [`lib/tesla_api/vehicle.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/tesla_api/vehicle.ex) within the `TeslaApi.Vehicle.handle_response/2` function. This private function pattern matches on the Tesla HTTP client response, specifically looking for `%Tesla.Env{status: 429, headers: headers}`.

When a match occurs, the code extracts the `Retry-After` header to determine how long to wait before the next request.

```elixir

# Detecting a 429 response (TeslaApi.Vehicle)

defp handle_response({:ok, %Tesla.Env{} = env}, opts) do
  case env do
    %Tesla.Env{status: 429, headers: headers} ->
      retry_after =
        case Enum.find(headers, fn {k, _} -> k == "retry-after" end) do
          nil   -> "300"
          {"retry-after", v} -> v
        end

      {:error,
       %Error{reason: :too_many_request, message: String.to_integer(retry_after)}}
    # … other clauses …

  end
end

```

## Extracting the Retry-After Header

The extraction logic scans the response headers for the `"retry-after"` key. If the header is absent, TeslaMate defaults to **300 seconds** (5 minutes) as a conservative back-off period.

The function converts the header value to an integer and wraps it in a custom `%Error{}` struct with the reason `:too_many_request` and the retry seconds stored in the message field. This creates a type-safe error that propagates through the functional call chain.

## Error Propagation and Logging

In [`lib/teslamate/api.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/teslamate/api.ex), the `Teslamate.Api.handle_result/3` function consumes the `%TeslaApi.Error{}` struct. When it encounters `reason: :too_many_request`, it logs a warning message containing the retry delay and forwards a normalized tuple `{:error, :too_many_request, retry_seconds}` to calling processes.

This centralized handling ensures consistent logging across the application while providing callers with the specific back-off interval required by the Tesla API.

```elixir

# Propagating the error (Teslamate.Api)

defp handle_result(result, _auth, _name) do
  case result do
    {: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}
    # … other clauses …

  end
end

```

## Implementing Back-Off in Calling Code

Higher-level modules such as [`lib/teslamate/vehicles/vehicle.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/teslamate/vehicles/vehicle.ex) receive the error tuple and implement appropriate back-off strategies. Callers can use `Process.send_after/3` to schedule retries after the specified interval, preventing request spam while respecting server-defined limits.

```elixir

# Using the retry interval (example caller)

case Teslamate.Api.some_call(...) do
  {:error, :too_many_request, wait_secs} ->
    # back‑off before next attempt

    Process.send_after(self(), :retry_request, wait_secs * 1_000)
  {:ok, data} ->
    # normal processing

    …
end

```

## Summary

- TeslaMate detects HTTP 429 responses in `TeslaApi.Vehicle.handle_response/2` by pattern matching on the status code.
- The `Retry-After` header is extracted from the response, defaulting to 300 seconds if not present.
- Errors propagate as `{:error, :too_many_request, retry_seconds}` through `Teslamate.Api.handle_result/3`.
- The application logs rate limit incidents with the specific delay value for observability.
- Calling code uses the retry interval to implement non-blocking back-off strategies.

## Frequently Asked Questions

### What happens when TeslaMate encounters an HTTP 429 error?

When the Tesla Owner API returns HTTP 429, TeslaMate extracts the `Retry-After` header value and returns a structured error tuple containing the exact number of seconds to wait. This prevents the application from immediately retrying the request and potentially exacerbating the rate limit.

### Where does TeslaMate extract the retry delay from?

The retry delay is extracted from the `Retry-After` HTTP response header in [`lib/tesla_api/vehicle.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/tesla_api/vehicle.ex). If this header is missing, the system defaults to a 300-second back-off period to ensure safe operation.

### How does TeslaMate avoid spamming the Tesla API during rate limiting?

TeslaMate avoids spamming by respecting the server-defined back-off interval. When `Teslamate.Api.handle_result/3` detects a rate limit error, it logs a warning and forwards the retry delay to calling processes, which can then schedule delayed retries using `Process.send_after/3` or similar mechanisms.

### What is the default back-off period if no Retry-After header is present?

If the Tesla API response does not include a `Retry-After` header, TeslaMate defaults to **300 seconds** (5 minutes) as the retry interval. This conservative default ensures compliance with API limits while maintaining data ingestion continuity.