Spaces:
Sleeping
Sleeping
""" | |
Background price cache with multi-API fallback and rate-limit handling | |
""" | |
import httpx | |
import logging | |
# Primary and secondary APIs for crypto prices | |
COINGECKO_URL = ( | |
"https://api.coingecko.com/api/v3/simple/price" | |
"?ids=bitcoin,ethereum,dogecoin&vs_currencies=usd" | |
) | |
COINCAP_URL = ( | |
"https://api.coincap.io/v2/assets?ids=bitcoin,ethereum,dogecoin" | |
) | |
# Shared price cache | |
CURRENT_PRICES = {"bitcoin": "--", "ethereum": "--", "dogecoin": "--"} | |
# How often to retry each API before falling back (seconds) | |
RETRY_DELAY = 5 | |
def fetch_prices() -> None: | |
""" | |
Try CoinGecko first; on 429 or error, fall back to CoinCap. | |
Updates CURRENT_PRICES in-place. | |
""" | |
global CURRENT_PRICES | |
apis = [ | |
("CoinGecko", COINGECKO_URL), | |
("CoinCap", COINCAP_URL) | |
] | |
for name, url in apis: | |
try: | |
resp = httpx.get(url, timeout=10) | |
# Handle CoinCap JSON structure separately | |
if name == "CoinGecko": | |
resp.raise_for_status() | |
data = resp.json() | |
prices = { | |
"bitcoin": data["bitcoin"]["usd"], | |
"ethereum": data["ethereum"]["usd"], | |
"dogecoin": data["dogecoin"]["usd"] | |
} | |
else: | |
resp.raise_for_status() | |
data = resp.json().get("data", []) | |
prices = {item["id"]: float(item["priceUsd"]) for item in data} | |
CURRENT_PRICES.update(prices) | |
logging.info("✅ [%s] prices updated: %s", name, prices) | |
return | |
except httpx.HTTPStatusError as e: | |
status = e.response.status_code | |
if status == 429: | |
logging.warning("⚠️ [%s] rate limit (429). Retrying fallback.", name) | |
else: | |
logging.warning("⚠️ [%s] HTTP error %s: %s", name, status, e) | |
except Exception as e: | |
logging.warning("⚠️ [%s] fetch error: %s", name, e) | |
# If we reach here, wait a bit before trying next API | |
try: | |
import time; time.sleep(RETRY_DELAY) | |
except Exception: | |
pass | |
logging.error("❌ All price APIs failed. Keeping previous prices: %s", CURRENT_PRICES) | |