yfinance 1.6.0 was uploaded to PyPI on August 13, 2026. This tutorial covers historical prices, quote streaming, and screening for personal Python projects. Its editorial basis is the official 1.6.0 source and offline regression tests with synthetic data and mocked network responses, not a verified account of running a personal portfolio tracker.
Correction — September 18, 2026: Replaced the incorrect WebSocket and screener interfaces, corrected MultiIndex access and HTTP-backend roles, and made retries back off on empty results as well as exceptions. Removed unsupported usage anecdotes and stale FAQ markup. These checks do not establish successful live Yahoo data access.
On this page
Version and Data-Rights Boundaries#
These interfaces are available in 1.6.0, not necessarily new in that release. The examples below use EquityQuery to build equity filters and screen() to submit them, replacing the previous screener snippet with the interface tested here.
yfinance is not affiliated with or endorsed by Yahoo. The project warns that Yahoo Finance data is intended for personal use only. Check the applicable terms before use or redistribution, especially commercially; a software license is not a market-data license. This is an educational programming tutorial, not investment advice.
Use a project-specific Python environment and pin the version to reproduce these interfaces:
python -m pip install "yfinance==1.6.0"
The default install includes curl_cffi, the preferred HTTP backend. The versioned HTTP adapter falls back to requests if curl_cffi cannot be imported or YF_DISABLE_CURL_CFFI is set to 1, true, or yes before import. Either backend can be blocked or rate-limited; curl_cffi is not the fallback for requests.
Fetching Historical Data Without Shooting Yourself in the Foot#
Use Ticker for single-instrument history:
import yfinance as yf
msft = yf.Ticker("MSFT")
# Last 3 months of daily data
hist = msft.history(period="3mo", auto_adjust=True)
print(hist.tail())
# Specific date range; end is exclusive
hist2 = msft.history(
start="2026-01-01", end="2026-08-01", auto_adjust=True
)
print(hist2.shape)
For a successful daily equity response with the default actions setting, history() returns a pandas DataFrame with Open, High, Low, Close, Volume, Dividends, and Stock Splits columns. Its index is timezone-aware. Comparisons against timezone-naive datetime objects can raise an error: localize or convert your comparison timestamps deliberately rather than stripping the timezone without understanding the effect. Empty or failed responses need separate handling.
history() defaults to auto_adjust=True, adjusting OHLC using Yahoo’s adjusted-close data. Pass auto_adjust=False to retain Yahoo’s OHLC and separate Adj Close instead. That does not promise an untouched exchange tape or reverse every provider-side corporate-action adjustment. Choose the convention your analysis needs, record it, and check discrepancies against the other data source.
For fetching multiple tickers at once, use yf.download():
import yfinance as yf
tickers = ["AAPL", "GOOGL", "BRK-B", "VTI"]
data = yf.download(
tickers, period="1y", group_by="ticker", auto_adjust=True
)
if data is None or data.empty:
raise RuntimeError("No price rows returned; check the data source.")
close = data.xs("Close", axis=1, level="Price")
print(close.tail())
With group_by="ticker", the MultiIndex column levels are Ticker then Price. Selecting Close with xs() at the Price level produces one column per ticker. Alternatively, use group_by="column" and then data["Close"]. Do not mix ticker-first grouping with price-first indexing. Check each ticker for missing values before feeding the resulting table into a backtest.
Live Streaming and Where It Actually Helps#
For a personal project that wants quote updates, the synchronous WebSocket interface takes an optional server URL, not a ticker list. Subscribe to symbols and pass a callback to the blocking listen() method:
import yfinance as yf
ws = yf.WebSocket(verbose=False)
try:
ws.subscribe(["AAPL", "MSFT"])
ws.listen(message_handler=print)
finally:
ws.close()
listen() delivers decoded message dictionaries to the callback; it is not a stream() iterator. Press Ctrl+C to interrupt this terminal example. The finally block closes the connection when execution leaves the listening call, including on failure. The synchronous 1.6.0 listener can stop after a receive error; it does not provide a guaranteed reconnect-and-resubscribe loop.
The streaming data is subject to Yahoo’s feed availability, latency, and market hours. Do not assume complete ticks, exchange-certified real-time delivery, or automatic recovery. Test stale-message detection, disconnections, and shutdown on your own network before relying on it. The editorial regression test used a fake socket carrying a synthetic protobuf message, so it verifies this interface and cleanup, not current quote delivery.
For an async application, AsyncWebSocket offers awaited subscribe(), listen(), and close() methods. Recovery still needs application-level testing.
Screening Equities with EquityQuery#
EquityQuery and screen() let you send filter criteria upstream instead of downloading an entire universe and filtering it in pandas:
from yfinance import EquityQuery, screen
# Example filter, not a recommendation or a current market result
q = EquityQuery("and", [
EquityQuery("gt", ["intradaymarketcap", 10_000_000_000]),
EquityQuery("eq", ["sector", "Technology"]),
EquityQuery("eq", ["region", "us"]),
])
results = screen(
q, size=5, sortField="intradaymarketcap", sortAsc=False
)
print(results.get("quotes", [])[:5])
The valid market-cap query field is intradaymarketcap, not marketcap. This example restricts the region to the US; check field units and returned currency. Custom queries use size, with a maximum of 250; predefined screens use count. Check the query implementation, field definitions, and actual response before building logic on top.
If your permitted use includes local storage, a weekly screener snapshot in SQLite can make later analysis reproducible: save the retrieval time, query, version, and results. It does not guarantee avoidance of rate limits, and today’s screen is not a historical constituent list. Applying it retrospectively can introduce selection and look-ahead bias.
What the Library Cannot Do and What to Reach for Instead#
yfinance does not provide a guaranteed complete historical tick dataset, Level 2 order book depth, or historical intraday data beyond what Yahoo’s API exposes. For many personal finance projects and exploratory backtests, those limits may be acceptable. Precise intraday execution modeling needs appropriately licensed, validated market data rather than assumptions about this feed.
Do not assume a stable request allowance or that every failure raises an exception. Empty frames can reflect an invalid symbol, unavailable data, or an upstream problem. The bounded wrapper below delays after either an empty frame or an exception, but only when another attempt remains. Backoff is not a promise of access or a way to bypass restrictions:
import time
import yfinance as yf
def fetch_with_retry(ticker, retries=3, delay=2):
if retries < 1 or delay < 0:
raise ValueError("retries must be positive and delay nonnegative")
for attempt in range(retries):
try:
data = yf.Ticker(ticker).history(period="1y")
if not data.empty:
return data
print(f"Attempt {attempt + 1} returned no rows.")
except Exception as exc:
print(f"Attempt {attempt + 1} failed: {type(exc).__name__}")
if attempt + 1 < retries:
time.sleep(delay * (2 ** attempt))
return None
With the defaults, three unsuccessful attempts have waits of two and four seconds, not an extra sleep after the final attempt. Check for None before using the result. Repeated empties should trigger investigation, not an unbounded retry loop. Catching Exception is a compact tutorial choice; a larger application should distinguish transient failures from invalid input and record enough context to diagnose them.
A laptop or existing machine is sufficient to try these scripts; no hardware purchase is required.
A Practical Starting Project#
Try a weekly snapshot of a small permitted watchlist: save retrieval times, historical prices and missing-data checks to a local CSV. Record adjustment settings and handle splits, dividends and exchange calendars deliberately. Keep private holdings out of public channels; a price-return comparison is not a complete portfolio-performance calculation.
For separate financial-data API workflows, see the SEC EDGAR XBRL guide and Treasury FiscalData guide. Company filings and fiscal series are different datasets, not substitutes for traded quotes. Validate each source’s definitions, update schedule and usage terms before combining results.
Editorial Basis and Sources#
The checked PyPI wheel matches the relevant files at official tag 1.6.0, commit 93eb4c234acc7d0cf9d176e602b8443179546253. Sources include the WebSocket implementation, screen function, multi-ticker download implementation, and history implementation. Offline tests exercise the corrected code with yfinance 1.6.0; they do not certify live data availability, latency, completeness, investment results, or broker connectivity. No trades or broker requests were made.
Leave a Reply