we-promise/sure · error · Provider::Eodhd::Error

API error: #{parsed["error"]}

Error message

API error: #{parsed["error"]}

What it means

Raised as Provider::Eodhd::Error when a parsed EODHD response is a Hash containing a present 'error' key. EODHD signals failures in-band: HTTP status is often 200 while the JSON body carries {"error": "..."}. check_api_error! runs on every parsed response (search, EOD prices, fundamentals) before format validation, converting these in-band errors into a Ruby exception with the upstream message preserved verbatim.

Source

Thrown at app/models/provider/eodhd.rb:304

    # Uses atomic increment-then-check to avoid TOCTOU races between concurrent workers.
    def enforce_daily_limit!
      new_count = Rails.cache.increment(daily_cache_key, 1, expires_in: 24.hours).to_i

      if new_count > max_requests_per_day
        raise RateLimitError, "EODHD daily rate limit of #{max_requests_per_day} requests exhausted"
      end
    end

    # throttle_request and min_request_interval provided by RateLimitable

    def max_requests_per_day
      ENV.fetch("EODHD_MAX_REQUESTS_PER_DAY", MAX_REQUESTS_PER_DAY).to_i
    end

    def check_api_error!(parsed)
      return unless parsed.is_a?(Hash) && parsed["error"].present?

      raise Error, "API error: #{parsed["error"]}"
    end
end

View on GitHub (pinned to e69894adb9)

Solutions

  1. Read the message text: 'Invalid API key/Token' → fix credentials; 'Exceeded ... limit' → EODHD-side quota (distinct from the local RateLimitError); 'Not found' → bad symbol/exchange suffix
  2. Verify the token against EODHD's user/details endpoint to confirm validity and remaining quota
  3. For quota errors, upgrade the plan or reduce request volume (see the local daily limiter) — retrying will not help
  4. For symbol errors, re-run a search_securities lookup to get the correct ticker/exchange mapping
  5. Store the corrected token in the provider settings UI / credential store rather than ENV snapshots to avoid stale copies

Example fix

# before: token errors surface as a generic raised string, caller retries pointlessly
begin
  provider.fetch_security_prices(symbol: "AAPL.US", start_date: from, end_date: to)
rescue Provider::Eodhd::Error => e
  retry_job
end

# after: branch on the upstream message; only retry non-auth, non-quota failures
rescue Provider::Eodhd::Error => e
  raise if e.message.match?(/Invalid API key|Exceeded/i)
  retry_job if e.message.match?(/temporary|timeout/i)
Defensive patterns

Strategy: try-catch

Validate before calling

# Validate the token's account status before starting syncs
user_info = client.get("#{base_url}/api/user-details") # adjust per your client
raise if user_info.body.include?('"error"') # surface token problems up front

Try / catch

begin
  data = provider.fetch_security_prices(...)
rescue Provider::Eodhd::Error => e
  case e.message
  when /Invalid API key|Token/i then disconnect_provider("EODHD auth failed: #{e.message}")
  when /Exceeded|limit/i         then schedule_retry_tomorrow
  else raise
  end
end

Prevention

When it happens

Trigger: Any EODHD call whose body includes a non-blank 'error' field: invalid or expired api_token ('Invalid API key'), unknown symbol ('Not found'), exceeding the account's request quota ('Exceeded the daily requests limit'), or restricted access to a data feed the token does not include. Triggered by the same GETs as search_securities and fetch_security_prices.

Common situations: Expired or typo'd API token after credentials rotation, free-tier token hitting EODHD's own (separate from the local) request limit, requesting US exchanges on a plan without US data access, delisted symbols, and environments sharing one token across many app instances.

Related errors


AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21). Data as JSON: /api/errors/89753ea23709bb4c. Report an issue: GitHub.