yfinance Ticker Info Not Working: Fix Empty Dicts and Names
When yfinance ticker info is not working, .info calls Yahoo's quoteSummary endpoint, which is sparse or blocked for many non-US tickers while history() still works. Use fast_info and history_metadata for name and currency.
- Ticker.info reads the quoteSummary endpoint and history() reads the chart endpoint, so one can work while the other fails.
- fast_info['currency'] and history_metadata come from the chart data and are the most dependable source of currency, exchange and often the name.
- Always use info.get('longName') in a fallback chain, never info['longName'], because sparse responses simply omit keys.
- Separate 'no data exists' from 'blocked' by exception type, HTTP status and a MSFT control call in the same session.
- For a whole index, throttle, retry with backoff and cache results to disk so each symbol is fetched once.
Why is yfinance ticker info not working for AFT.NZ but fine for MSFT?
yfinance talks to two different Yahoo backends. history() and fast_info use the chart endpoint (v8/finance/chart), which needs only a symbol and returns prices plus a small metadata block. Ticker.info uses quoteSummary (v10/finance/quoteSummary) with modules such as assetProfile, summaryDetail, price, defaultKeyStatistics and financialData.
quoteSummary is populated from Yahoo's fundamentals and profile database. Large US names have full coverage. Smaller or non-US listings (NZX, ASX small caps, some Asian and European lines) often have a quote and chart but little or no profile data. The response then holds a handful of keys, or nothing.
quoteSummary also requires a cookie plus a crumb token, while chart is more lenient. When Yahoo changes the cookie or crumb flow, or throttles your IP, .info breaks first. That is why the same ticker can show a price chart in your code and an empty dict from .info.
What do the different symptoms mean?
The same call gives different failures for different reasons. Treating them all as 'upgrade yfinance' wastes time, so map the symptom to the cause first.
| Symptom | Likely cause | What to do |
|---|---|---|
| info == {} or {'trailingPegRatio': None} | Yahoo has no quoteSummary profile for the symbol, or the symbol is wrong | Check the suffix, then use fast_info and history_metadata |
| Dict with only a few keys, no longName | Sparse profile data for a small or non-US listing | Fall back to shortName, then chart metadata, then Search |
| KeyError: 'longName' or 'shortName' | Indexing a dict that never had the key | Use .get() everywhere |
| HTTP 404 'Quote not found' | Symbol unknown to Yahoo, delisted, or a wrong suffix | Verify with yf.Search and the Yahoo site |
| 404 or 401 'Invalid Crumb' | Stale cookie or crumb, outdated yfinance, or a Yahoo-side change | Upgrade, start a fresh process, retry once |
| 429 Too Many Requests / YFRateLimitError | Too many requests from your IP | Back off, throttle, cache, pause for minutes |
| history() works but info fails | Different endpoints with different coverage and auth | Use chart-based fields for name and currency |
How do I check whether it is my setup or Yahoo's data?
Run these checks in order. They take a few minutes and separate the three failure classes: old library, blocked session and missing data.
- Upgrade: pip install -U yfinance. Yahoo changes its web flow often, and old releases break on crumb handling.
- Confirm the version in the interpreter you actually run: import yfinance; print(yfinance.__version__). Notebooks and virtualenvs often differ from the shell.
- Restart the process, so a stale cookie is discarded, and test MSFT and your ticker back to back in the same session.
- Open the quote page for the symbol on the Yahoo site and compare it with the Profile and Statistics tabs. If they are empty there, the data is absent at source.
- Run the control test below. If MSFT also fails, you are blocked or outdated. If only AFT.NZ fails, the data is missing.
import yfinance as yf
for sym in ["MSFT", "AFT.NZ"]:
t = yf.Ticker(sym)
try:
info = t.info
print(sym, "info keys:", len(info), "longName:", info.get("longName"))
except Exception as e:
print(sym, "info error:", type(e).__name__, e)
print(sym, "fast currency:", t.fast_info["currency"])
print(sym, "history rows:", len(t.history(period="5d"))) How do I get company name and currency without relying on .info?
Two chart-backed sources avoid quoteSummary. t.fast_info is a lightweight mapping with keys such as currency, exchange and last_price. t.history_metadata returns the chart metadata dict, which typically includes currency, exchangeName, instrumentType, symbol, and often longName and shortName.
history_metadata is populated by a history call, so call t.history(period="1d") first if you get an empty result. Whether longName is present depends on the symbol, so treat every key as optional.
import yfinance as yf
t = yf.Ticker("AFT.NZ")
print(t.fast_info["currency"], t.fast_info["exchange"], t.fast_info["last_price"])
t.history(period="1d") # ensures metadata is loaded
meta = t.history_metadata or {}
print(meta.get("currency"), meta.get("exchangeName"),
meta.get("longName"), meta.get("shortName"))What is a reliable get_name_currency function with a fallback chain?
The layered order is info, then fast_info, then history_metadata, then Search. Each layer only fills fields still missing, every access uses .get(), and each exception is recorded rather than swallowed. A 'source' field shows where each value came from, which matters when you audit a stock list later.
yf.Search(symbol).quotes returns a list of dicts with fields such as symbol, shortname, longname, exchange and exchDisp. It is a separate search endpoint, so it can name a ticker that .info cannot. Match on the exact symbol, not the first result.
import yfinance as yf
def get_name_currency(symbol, use_search=True):
out = {"symbol": symbol, "name": None, "currency": None,
"exchange": None, "source": {}, "errors": []}
t = yf.Ticker(symbol)
def fill(key, value, src):
if value and out[key] is None:
out[key] = value
out["source"][key] = src
try:
info = t.info or {}
fill("name", info.get("longName") or info.get("shortName"), "info")
fill("currency", info.get("currency"), "info")
fill("exchange", info.get("exchange"), "info")
except Exception as e:
out["errors"].append(f"info:{type(e).__name__}")
try:
fi = t.fast_info
fill("currency", fi.get("currency"), "fast_info")
fill("exchange", fi.get("exchange"), "fast_info")
except Exception as e:
out["errors"].append(f"fast_info:{type(e).__name__}")
try:
t.history(period="1d")
m = t.history_metadata or {}
fill("name", m.get("longName") or m.get("shortName"), "history_metadata")
fill("currency", m.get("currency"), "history_metadata")
fill("exchange", m.get("exchangeName"), "history_metadata")
except Exception as e:
out["errors"].append(f"history:{type(e).__name__}")
if use_search and out["name"] is None:
try:
for q in yf.Search(symbol).quotes:
if q.get("symbol", "").upper() == symbol.upper():
fill("name", q.get("longname") or q.get("shortname"), "search")
fill("exchange", q.get("exchDisp") or q.get("exchange"), "search")
break
except Exception as e:
out["errors"].append(f"search:{type(e).__name__}")
return out
print(get_name_currency("AFT.NZ"))
# Illustrative shape of a result (values depend on what Yahoo returns that day):
# {'symbol': 'AFT.NZ', 'name': 'AFT Pharmaceuticals Limited', 'currency': 'NZD',
# 'exchange': 'NZE', 'source': {'name': 'history_metadata', 'currency': 'fast_info',
# 'exchange': 'fast_info'}, 'errors': []}How do I run this for a whole index like the NZX50 without getting blocked?
Yahoo's API is unofficial and its terms restrict automated bulk use, so keep volume low. Every symbol costs several requests in the function above, which is why 429 errors appear in the middle of a 50-symbol loop.
Cache first, throttle second, retry third. Metadata such as name and currency changes rarely, so fetch each symbol once and reuse the file. Retry only transient errors (429, timeouts) with exponential backoff and jitter, and never retry a 404, which is a permanent answer.
import json, os, random, time
CACHE = "names_currency.json"
cache = json.load(open(CACHE)) if os.path.exists(CACHE) else {}
def with_backoff(sym, tries=4):
for i in range(tries):
res = get_name_currency(sym)
limited = any("RateLimit" in e or "429" in e or "TooMany" in e
for e in res["errors"])
if not limited:
return res
time.sleep(2 ** i * 5 + random.random())
return res
symbols = ["AFT.NZ", "AIA.NZ", "FPH.NZ", "MFT.NZ"] # build from your NZX50 list
for s in symbols:
if s in cache and cache[s]["name"] and cache[s]["currency"]:
continue
cache[s] = with_backoff(s)
json.dump(cache, open(CACHE, "w"), indent=1)
time.sleep(1.5 + random.random()) How do I tell missing data from a block, and what if Yahoo lacks it?
Missing data is stable: the same ticker returns the same sparse result across retries, while the MSFT control works in the same session. A block is intermittent or global: MSFT fails too, the error is 429 or Invalid Crumb, and it clears after waiting or restarting. Newer yfinance versions raise YFRateLimitError, which you can catch by name via getattr(yfinance.exceptions, 'YFRateLimitError', ()).
Be careful with a hardcoded suffix map, since it is a last resort and not a source of truth. Cross-listings, dual-currency lines and quote-unit quirks break it. Use it only to fill a currency flagged as 'assumed'.
If Yahoo cannot supply the fields, use a better source for the identity data: the exchange's own listings file (NZX publishes its instrument list), OpenFIGI to map tickers to names and exchange codes, or a paid market-data provider with a licence that permits your use.
| Suffix | Exchange | Currency (usual) | Pitfall |
|---|---|---|---|
| .NZ | NZX | NZD | Some names are dual-listed on ASX |
| .AX | ASX | AUD | Foreign-currency ETFs possible |
| .L | London | GBp | Quoted in pence, not pounds |
| .TA | Tel Aviv | ILA | Agorot, 100 per shekel |
| .TO | Toronto | CAD | US-dollar lines exist |
Common questions
Why does yfinance info return an empty dict?
Either Yahoo has no quoteSummary data for that symbol, or the request was rejected through a bad crumb or rate limit. Test MSFT in the same session: if it also fails you are blocked or outdated, and if only your ticker fails the data is missing or the suffix is wrong.
Is fast_info a full replacement for info?
No. fast_info exposes a small set such as currency, exchange, last_price and market cap, and has no company name, sector or fundamentals. It is the dependable choice for currency and exchange, and the name must come from history_metadata or Search.
Why does history_metadata sometimes have no longName?
The chart endpoint's metadata is not guaranteed to carry every field for every instrument, and smaller listings often only carry shortName or neither. Keep the chain going to yf.Search before you give up and mark the name as unknown.
Does the .NZ suffix always mean NZD?
Normally yes, because NZX lines are quoted in New Zealand dollars. Still, read currency from fast_info first and use the suffix map only as a flagged last resort, since hardcoding fails for pence or agorot quotes and unusual listings.
Can I use yfinance data in a commercial product?
Yahoo does not offer an official public API, and yfinance scrapes endpoints intended for its own site, so its terms restrict redistribution and heavy automated use. For a product, use a licensed data provider and keep yfinance for personal research.