{"record":{"id":"e2f61c05afe28053","repo":"we-promise/sure","slug":"alpha-vantage-daily-request-limit-reached-max-r","errorCode":null,"errorMessage":"Alpha Vantage daily request limit reached (#{max_requests_per_day} per day)","messagePattern":"Alpha Vantage daily request limit reached \\(#(.+?) per day\\)","errorType":"exception","errorClass":"Provider::AlphaVantage::RateLimitError","httpStatus":null,"severity":"warning","filePath":"app/models/provider/alpha_vantage.rb","lineNumber":251,"sourceCode":"\n        faraday.request :json\n        faraday.response :raise_error\n        faraday.params[\"apikey\"] = api_key\n      end\n    end\n\n    # Adds daily request counter on top of the interval throttle from RateLimitable.\n    def throttle_request\n      super\n\n      # Global per-day request counter via cache (Redis).\n      # Atomic increment-then-check avoids the TOCTOU of read-check-increment.\n      day_key = \"alpha_vantage:daily:#{Date.current}\"\n      new_count = Rails.cache.increment(day_key, 1, expires_in: 24.hours).to_i\n\n      if new_count > max_requests_per_day\n        Rails.logger.warn(\"AlphaVantage: daily request limit reached (#{new_count}/#{max_requests_per_day})\")\n        raise RateLimitError, \"Alpha Vantage daily request limit reached (#{max_requests_per_day} per day)\"\n      end\n    end\n\n    def max_requests_per_day\n      ENV.fetch(\"ALPHA_VANTAGE_MAX_REQUESTS_PER_DAY\", MAX_REQUESTS_PER_DAY).to_i\n    end\n\n    # Converts a symbol + MIC code to Alpha Vantage's ticker format\n    def to_av_symbol(symbol, exchange_operating_mic)\n      return symbol if exchange_operating_mic.blank?\n\n      suffix = MIC_TO_AV_SUFFIX[exchange_operating_mic]\n      return symbol if suffix.nil?\n      return symbol if suffix.empty?\n\n      # Avoid double-suffixing if the symbol already has the correct suffix\n      return symbol if symbol.end_with?(suffix)\n","sourceCodeStart":233,"sourceCodeEnd":269,"githubUrl":"https://github.com/we-promise/sure/blob/e69894adb92547273377398c15f45c979cd9416a/app/models/provider/alpha_vantage.rb#L233-L269","documentation":"Provider::AlphaVantage#throttle_request raises RateLimitError after a Redis(Rails.cache)-backed per-day counter (key alpha_vantage:daily:YYYY-MM-DD, atomic increment-then-check) exceeds max_requests_per_day — default 25 (free tier), overridable via ALPHA_VANTAGE_MAX_REQUESTS_PER_DAY. This is a local quota guard on top of the interval throttle; it fires before the HTTP call is made, so no request is wasted.","triggerScenarios":"More than max_requests_per_day provider calls in a UTC day across the whole app (the counter is global, not per-user): bulk price refreshes, security searches for many tickers, retry storms.","commonSituations":"Scheduled jobs syncing many securities; multiple families each triggering refreshes; a shared cache/Redis reset changing counts; default 25/day exhausted by mid-morning.","solutions":["Raise ALPHA_VANTAGE_MAX_REQUESTS_PER_DAY if you have a paid key with a higher quota.","Reduce call volume: cache prices, batch work, or move to a provider with a higher limit.","Check provider.usage (used vs limit) before starting a batch and skip when utilization is high.","Rescue Provider::AlphaVantage::RateLimitError and reschedule the job for the next day (counter key rolls at midnight UTC per Date.current)."],"exampleFix":"# before\nprovider.fetch_security_prices(symbol: s, start_date: from, end_date: to)\n\n# after\nusage = provider.usage\nif usage.utilization < 90\n  provider.fetch_security_prices(symbol: s, start_date: from, end_date: to)\nelse\n  PriceSyncJob.set(wait_until: Date.tomorrow.midnight + 5.minutes).perform_later(s)\nend","handlingStrategy":"retry","validationCode":"usage = provider.usage\nusage.used < usage.limit # cheap pre-check; counter key is alpha_vantage:daily:<date>","typeGuard":null,"tryCatchPattern":"begin\n  provider.fetch_security_prices(symbol: s, start_date: a, end_date: b)\nrescue Provider::AlphaVantage::RateLimitError\n  PriceSyncJob.set(wait_until: Date.tomorrow.midnight).perform_later(s) # counter rolls daily\nend","preventionTips":["Check provider.usage before starting batches; stop before exhausting the quota.","Cache aggressively — every AV call counts, even failed lookups.","Tune ALPHA_VANTAGE_MAX_REQUESTS_PER_DAY to the actual plan quota."],"tags":["alpha-vantage","rate-limit","quota","provider","ruby"],"backgroundTag":"api-rate-limit-exceeded","analyzedSha":"e69894adb92547273377398c15f45c979cd9416a","analyzedAt":"2026-08-21T18:22:41.165Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}