{"record":{"id":"549abc33f3e2476c","repo":"ZhuLinsen/daily_stock_analysis","slug":"alphavantage-rate-limited-data-note","errorCode":null,"errorMessage":"[AlphaVantage] Rate limited: {data['Note']}","messagePattern":"\\[AlphaVantage\\] Rate limited: (.+?)","errorType":"exception","errorClass":"DataFetchError","httpStatus":null,"severity":"warning","filePath":"data_provider/alphavantage_fetcher.py","lineNumber":64,"sourceCode":"\n        symbol = stock_code.strip().upper()\n        params = {\n            'function': 'TIME_SERIES_DAILY',\n            'symbol': symbol,\n            'outputsize': 'compact',\n            'apikey': self._api_key,\n        }\n\n        try:\n            self.random_sleep(0.5, 1.5)\n            resp = requests.get(_AV_BASE_URL, params=params, timeout=30)\n            resp.raise_for_status()\n            data = resp.json()\n        except Exception as e:\n            raise DataFetchError(f\"[AlphaVantage] HTTP request failed for {symbol}: {e}\") from e\n\n        if 'Note' in data:\n            raise DataFetchError(f\"[AlphaVantage] Rate limited: {data['Note']}\")\n        if 'Error Message' in data:\n            raise DataFetchError(f\"[AlphaVantage] API error for {symbol}: {data['Error Message']}\")\n\n        ts_key = 'Time Series (Daily)'\n        if ts_key not in data or not data[ts_key]:\n            raise DataFetchError(f\"[AlphaVantage] No time series data for {symbol}\")\n\n        rows = []\n        start = datetime.strptime(start_date, '%Y-%m-%d').date()\n        end = datetime.strptime(end_date, '%Y-%m-%d').date()\n        for date_str, values in data[ts_key].items():\n            row_date = datetime.strptime(date_str, '%Y-%m-%d').date()\n            if start <= row_date <= end:\n                rows.append({\n                    'date': date_str,\n                    '1. open': float(values.get('1. open', 0)),\n                    '2. high': float(values.get('2. high', 0)),\n                    '3. low': float(values.get('3. low', 0)),","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/ZhuLinsen/daily_stock_analysis/blob/5159bd72e8373d215492dff122acc9d389e219c9/data_provider/alphavantage_fetcher.py#L46-L82","documentation":"A DataFetchError raised by AlphaVantageFetcher._fetch_raw_data when the successful JSON response contains a top-level 'Note' key. AlphaVantage signals rate limiting this way — HTTP 200 with an explanatory Note instead of time-series data — so the fetcher converts it into an explicit 'Rate limited' error carrying the API's own message.","triggerScenarios":"Exceeding the AlphaVantage quota for your key tier (classic free tier: 25 requests/day; historically 5/minute): the 26th call in a day, or a burst of calls within a minute, returns {'Note': '...rate limit...'}. Note the built-in random_sleep(0.5, 1.5) is far below the historical 5-req/min pace, so bursts can trigger it.","commonSituations":"Free-tier key used in batch backfills over hundreds of symbols; multiple processes sharing one key; tests hitting the live API without caching. Also note: this error is NOT a RateLimitError subclass here — it is a plain DataFetchError, so generic rate-limit handling keyed on RateLimitError will miss it.","solutions":["Check the Note text: 'per minute' limits clear in ~60s and are worth a retry; daily quota requires waiting or a paid key.","Add caching (AV data only changes daily) and a per-minute request counter to stay under 5/min.","Upgrade to a premium key or spread batch fetches across the day.","Fall back to YfinanceFetcher/Akshare for US symbols while the AV quota resets."],"exampleFix":"# before\nfor sym in us_syms:\n    df = av_fetcher.fetch(sym, start, end)  # burns daily quota\n\n# after\nfor i, sym in enumerate(us_syms):\n    try:\n        df = av_fetcher.fetch(sym, start, end)\n    except DataFetchError as e:\n        if 'Rate limited' in str(e):\n            time.sleep(60)\n            df = yfinance_fetcher.fetch(sym, start, end)\n        else:\n            raise","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    df = av_fetcher.fetch(sym, start, end)\nexcept DataFetchError as e:\n    if 'Rate limited' in str(e):\n        if 'per minute' in str(e):\n            time.sleep(60)\n            df = av_fetcher.fetch(sym, start, end)\n        else:  # daily quota exhausted\n            df = yfinance_fetcher.fetch(sym, start, end)\n    else:\n        raise","preventionTips":["Cache AV responses — daily data does not change intraday, so fetch each symbol at most once per day.","Track your key's request count in-process and stop before hitting the tier limit.","Remember this is a plain DataFetchError, not RateLimitError — rate-limit handling keyed on the subclass will miss it."],"tags":["alphavantage","rate-limit","quota","http-200-error"],"backgroundTag":null,"analyzedSha":"5159bd72e8373d215492dff122acc9d389e219c9","analyzedAt":"2026-08-15T01:59:36.292Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}