we-promise/sure · warning · Provider::TwelveData::RateLimitError
429
429
Error message
Rate limit exceeded
What it means
Provider::TwelveData::RateLimitError raised by check_api_error! when the parsed response body carries code 429. Twelve Data signals rate limiting inside a 200-status JSON body ({code: 429, message: ...}), so this guard fires before the raise_error Faraday middleware ever sees an HTTP 429. The message is Twelve Data's own (e.g. 'You have exceeded your daily/monthly quota') or the default 'Rate limit exceeded'.
Source
Thrown at app/models/provider/twelve_data.rb:329
# Set timestamp after all waits so the next call's 1s pacing is measured
# from when this request actually fires, not from before the minute wait.
@last_request_time = Time.current
end
def min_request_interval
ENV.fetch("TWELVE_DATA_MIN_REQUEST_INTERVAL", MIN_REQUEST_INTERVAL).to_f
end
def max_requests_per_minute
ENV.fetch("TWELVE_DATA_MAX_REQUESTS_PER_MINUTE", 7).to_i
end
def check_api_error!(parsed)
return unless parsed.is_a?(Hash) && parsed["code"].present?
if parsed["code"] == 429
raise RateLimitError, parsed["message"] || "Rate limit exceeded"
end
raise Error, "API error (code: #{parsed["code"]}): #{parsed["message"] || "Unknown error"}"
end
def default_error_transformer(error)
case error
when RateLimitError
error
when Faraday::TooManyRequestsError
RateLimitError.new("TwelveData rate limit exceeded", details: error.response&.dig(:body))
when Faraday::Error
self.class::Error.new(error.message, details: error.response&.dig(:body))
else
self.class::Error.new(error.message)
end
end
endView on GitHub (pinned to e69894adb9)
Solutions
- Upgrade the Twelve Data plan or reduce request volume (cache results, batch lookups) if the daily quota is exhausted — waiting won't help until quota reset
- Align TWELVE_DATA_MAX_REQUESTS_PER_MINUTE with your plan's real per-minute limit so the built-in throttle works
- For per-minute bursts, back off ~60s and retry once; ensure all processes share the same Rails.cache so the credit counter is global
- Monitor provider.usage (daily_usage vs plan_daily_limit) before starting large syncs
Example fix
// before
rates = provider.fetch_exchange_rates(from: "EUR", to: "USD", start_date: s, end_date: e)
// after
begin
rates = provider.fetch_exchange_rates(from: "EUR", to: "USD", start_date: s, end_date: e)
rescue Provider::TwelveData::RateLimitError => e
raise if e.message.include?("daily") # daily quota: stop, don't retry
sleep 60
retry
end Defensive patterns
Strategy: retry
Validate before calling
usage = provider.usage if usage.utilization >= 95 # defer non-critical syncs before hitting the daily quota end
Try / catch
begin provider.fetch_exchange_rates(from:, to:, start_date:, end_date:) rescue Provider::TwelveData::RateLimitError => e raise if e.message =~ /daily|monthly/ # quota exhausted: retrying now is pointless sleep 60 retry end
Prevention
- Set TWELVE_DATA_MAX_REQUESTS_PER_MINUTE to the plan's real limit so the built-in throttle protects you
- Share Rails.cache across processes so the per-minute credit counter is global
- Check provider.usage before large syncs and cache aggressively — the cross-listing fallback costs 5 credits per call
When it happens
Trigger: Exceeding the per-minute credit limit (default 8/min on free plan; the client's throttle assumes 7), burning the daily quota (8 credits/day free tier), or the 5-credit time_series/cross call jumping past the remaining balance. Parallel processes each running their own throttle can combine to breach it.
Common situations: Free-plan daily quota exhausted mid-sync; multiple app instances/threads sharing one API key with independent throttle counters (the cache counter is only shared when Rails.cache is shared); the cross-listing fallback consuming 5 credits unexpectedly.
Related errors
- Alpha Vantage daily request limit reached (#{max_requests_pe
- rate_limited
- rate_limited
- parsed["Note"]
- EODHD daily rate limit of #{max_requests_per_day} requests e
AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21).
Data as JSON: /api/errors/d132c962a346b65e.
Report an issue: GitHub.