Back to Docs

Rate Limits

Understanding API rate limits and how to handle them.

Rate Limits

SheetsJSON enforces rate limits to ensure fair usage and platform stability.

Limits by Plan

Plan Requests/minute Requests/day
Free 60 1,000
Pro 300 50,000
Business 1,000 500,000
Enterprise Custom Custom

Rate Limit Headers

Every response includes rate limit information:

HTTP/1.1 200 OK
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 1705764000
Header Description
X-RateLimit-Limit Maximum requests allowed in the window
X-RateLimit-Remaining Remaining requests in current window
X-RateLimit-Reset Unix timestamp when the window resets

Handling Rate Limits

When you exceed the rate limit, you’ll receive a 429 Too Many Requests response:

{
  "error": {
    "code": "rate_limited",
    "message": "Rate limit exceeded. Please retry after 45 seconds.",
    "retry_after": 45
  }
}

Best Practices

1. Implement Exponential Backoff

defmodule MyApp.SheetsClient do
  def fetch_with_retry(url, opts \\ []) do
    max_retries = Keyword.get(opts, :max_retries, 3)
    do_fetch(url, 0, max_retries)
  end

  defp do_fetch(_url, attempt, max) when attempt >= max do
    {:error, :max_retries_exceeded}
  end

  defp do_fetch(url, attempt, max) do
    case Req.get(url) do
      {:ok, %{status: 429, headers: headers}} ->
        retry_after = get_retry_after(headers)
        Process.sleep(retry_after * 1000)
        do_fetch(url, attempt + 1, max)

      {:ok, response} ->
        {:ok, response}

      {:error, reason} ->
        {:error, reason}
    end
  end

  defp get_retry_after(headers) do
    headers
    |> Enum.find(fn {k, _} -> String.downcase(k) == "retry-after" end)
    |> case do
      {_, value} -> String.to_integer(value)
      nil -> :math.pow(2, 1) |> round()
    end
  end
end

2. Cache Responses

Reduce API calls by caching responses:

defmodule MyApp.Cache do
  use GenServer

  def get_sheet(sheet_id) do
    case Cachex.get(:sheets_cache, sheet_id) do
      {:ok, nil} ->
        data = fetch_from_api(sheet_id)
        Cachex.put(:sheets_cache, sheet_id, data, ttl: :timer.minutes(5))
        data

      {:ok, data} ->
        data
    end
  end
end

3. Use Webhooks

Instead of polling for changes, use webhooks to receive real-time updates:

# Instead of polling every minute
def poll_for_changes do
  # Don't do this
end

# Use webhooks
post "/webhooks/sheetsjson" do
  handle_sheet_update(params)
end

Monitoring Usage

Track your API usage in the dashboard:

  1. Go to SettingsUsage
  2. View requests by day, week, or month
  3. Set up alerts for approaching limits

Need Higher Limits?

If you consistently hit rate limits, consider:

  • Optimizing your requests — Use caching and webhooks
  • Upgrading your plan — Higher tiers have increased limits
  • Contacting us — Enterprise plans have custom limits

Contact Sales for enterprise pricing.

Was this page helpful? |