--- name: finance-search description: "Free finance APIs for stock, ETF, and mutual fund data — yfinance (no key), Alpha Vantage, Polygon.io, Twelve Data. Multi-backend with automatic fallback." version: 1.1.0 author: Hermes Agent metadata: hermes: tags: [finance, stocks, etf, mutual-funds, yfinance, alpha-vantage, polygon, twelve-data] --- # finance-search — Free Finance APIs Multi-backend finance data skill. Primary: yfinance (no API key). Fallbacks: Alpha Vantage, Polygon.io, Twelve Data (free keys required). ## Backends | Backend | Key Required | Rate Limit | Best For | |---------|-------------|------------|----------| | **yfinance** | No | IP-based (Yahoo) | Stock/ETF/mutual fund prices, history, info | | **Alpha Vantage** | Free key | 25/day | Historical data, fundamentals, FX | | **Polygon.io** | Free key | 5/min | Real-time prices, ticker details | | **Twelve Data** | Free key | 800/day | Historical data, technical indicators | ## Setup ### yfinance (no key — works immediately) ```bash pip install yfinance ``` ### Alpha Vantage (free key) 1. Go to https://www.alphavantage.co/support/#api-key 2. Fill in name, email, organization (any values work) 3. Click "GET FREE API KEY" 4. Copy the key from the page 5. Set env: `export ALPHA_VANTAGE_API_KEY=your_key` ### Polygon.io (free key) 1. Go to https://polygon.io/dashboard/signup 2. Sign up with email and password 3. Verify email (check inbox for confirmation link) 4. Log in, go to Dashboard → API Keys 5. Copy the "Default" key 6. Set env: `export POLYGON_API_KEY=your_key` ### Twelve Data (free key) 1. Go to https://twelvedata.com/apikey 2. Click "Get Free API Key" 3. Sign up with email or Google/GitHub 4. Key is shown immediately on the dashboard 5. Set env: `export TWELVE_DATA_API_KEY=your_key` ## Getting API Keys — Agent Directive **When a backend fails because no API key is set, do NOT just report the failure.** Proactively obtain a key: 1. Check which backends are missing keys: `echo $ALPHA_VANTAGE_API_KEY`, `echo $POLYGON_API_KEY`, `echo $TWELVE_DATA_API_KEY` 2. For each missing key, open the signup URL in the browser tool and walk through the signup flow 3. Save each key to `~/.hermes/.env` and the active profile's `.env`: ```bash echo 'ALPHA_VANTAGE_API_KEY=abc123' >> ~/.hermes/.env echo 'ALPHA_VANTAGE_API_KEY=abc123' >> ~/.hermes/profiles/general/.env ``` 4. Verify: `curl -s "https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=IBM&apikey=$ALPHA_VANTAGE_API_KEY" | python3 -c "import sys,json; d=json.load(sys.stdin); print('OK' if 'Global Quote' in d else 'FAIL: '+str(d))"` **Priority order for obtaining keys:** Twelve Data first (800/day, best free tier), then Polygon (5/min, good for spot checks), then Alpha Vantage (25/day, last resort). Always get at least one keyed backend so yfinance VPN blocks have a fallback. ## Usage ### Stock/ETF/Mutual Fund Performance ```python import yfinance as yf # Single ticker ticker = yf.Ticker("FZROX") hist = ticker.history(period="1y") info = ticker.info # Key fields print(f"Name: {info.get('longName')}") print(f"Category: {info.get('category')}") print(f"Expense Ratio: {info.get('annualReportExpenseRatio')}") print(f"1Y Return: {((hist['Close'].iloc[-1] - hist['Close'].iloc[0]) / hist['Close'].iloc[0]) * 100:.2f}%") ``` ### Multi-Ticker Comparison ```python import yfinance as yf import json tickers = { "FZROX": "Fidelity ZERO Total Market", "FNILX": "Fidelity ZERO Large Cap", "FZIPX": "Fidelity ZERO Extended Market", "FZILX": "Fidelity ZERO International", } results = {} for t, name in tickers.items(): f = yf.Ticker(t) h = f.history(period="1y") if not h.empty: ret = ((h['Close'].iloc[-1] - h['Close'].iloc[0]) / h['Close'].iloc[0]) * 100 results[t] = { "name": name, "return_1y_pct": round(ret, 2), "start": round(h['Close'].iloc[0], 2), "end": round(h['Close'].iloc[-1], 2), "expense_ratio": f.info.get('annualReportExpenseRatio', 'N/A'), } print(json.dumps(results, indent=2)) ``` ### Alpha Vantage Fallback ```python import os, requests key = os.environ.get("ALPHA_VANTAGE_API_KEY") if key: url = f"https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&symbol=FZROX&apikey={key}" data = requests.get(url).json() ``` ### Polygon.io Fallback ```python import os, requests key = os.environ.get("POLYGON_API_KEY") if key: url = f"https://api.polygon.io/v2/aggs/ticker/FZROX/prev?apiKey={key}" data = requests.get(url).json() ``` ### Twelve Data Fallback ```python import os, requests key = os.environ.get("TWELVE_DATA_API_KEY") if key: url = f"https://api.twelvedata.com/time_series?symbol=FZROX&interval=1day&outputsize=365&apikey={key}" data = requests.get(url).json() ``` ## Auto-Fallback Pattern Try backends in order. First success wins: ```python import yfinance as yf, os, requests, json def get_fund_performance(ticker, period="1y"): # 1. Try yfinance (no key) try: f = yf.Ticker(ticker) h = f.history(period=period) if not h.empty: ret = ((h['Close'].iloc[-1] - h['Close'].iloc[0]) / h['Close'].iloc[0]) * 100 return {"source": "yfinance", "ticker": ticker, "return_pct": round(ret, 2)} except: pass # 2. Try Alpha Vantage key = os.environ.get("ALPHA_VANTAGE_API_KEY") if key: try: url = f"https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&symbol={ticker}&outputsize=full&apikey={key}" r = requests.get(url, timeout=10) if r.status_code == 200: return {"source": "alphavantage", "ticker": ticker, "raw": r.json()} except: pass # 3. Try Polygon key = os.environ.get("POLYGON_API_KEY") if key: try: url = f"https://api.polygon.io/v2/aggs/ticker/{ticker}/prev?apiKey={key}" r = requests.get(url, timeout=10) if r.status_code == 200: return {"source": "polygon", "ticker": ticker, "raw": r.json()} except: pass # 4. Try Twelve Data key = os.environ.get("TWELVE_DATA_API_KEY") if key: try: url = f"https://api.twelvedata.com/time_series?symbol={ticker}&interval=1day&outputsize=365&apikey={key}" r = requests.get(url, timeout=10) if r.status_code == 200: return {"source": "twelvedata", "ticker": ticker, "raw": r.json()} except: pass return {"error": f"All backends failed for {ticker}"} ``` ## Common Fidelity Zero Tickers | Ticker | Name | |--------|------| | FZROX | Fidelity ZERO Total Market Index | | FNILX | Fidelity ZERO Large Cap Index | | FZIPX | Fidelity ZERO Extended Market Index | | FZILX | Fidelity ZERO International Index | ## Pitfalls - **yfinance blocked on VPN IPs.** Yahoo rate-limits shared/VPN IPs. If `fc.yahoo.com` connection refused, rotate VPN (`nordvpn` skill) or use a keyed backend. - **Mutual fund data is delayed.** Yahoo Finance mutual fund prices are typically T+1. Don't expect real-time. - **Alpha Vantage free tier is 25 requests/day.** Don't use for bulk queries. - **Polygon free tier is 5 requests/minute.** Good for spot checks, not streaming. - **Twelve Data free tier is 800 requests/day.** Best free tier for volume. - **Some tickers may be delisted or unavailable.** yfinance will warn. Check the ticker on Yahoo Finance first.