How to get a history of market cap and P/E, not today's snapshot
yfinance's info gives today's market cap and P/E. A history needs share counts as filed, carried forward between filings, or a vendor that did it daily.
yfinance's info dictionary holds today's market cap and P/E, not a history. A history is each day's unadjusted price times the share count known that day, and counts arrive on filing cover pages quarterly, weeks after the date they describe. Sharadar's daily table and Financial Modeling Prep's market-cap endpoint return the series ready-made; SEC EDGAR's free XBRL API gives the counts and earnings to build it. Share classes, splits and restatements break it.
The short way
The obvious call is the wrong one. In yfinance, Ticker("AAPL").info["marketCap"] and
info["trailingPE"] are today's figures; on 9 October 2026 they were $4.97 trillion and 39.0.
Ask for them in a loop over past dates and you get the same number every time.
A historical market cap is two series multiplied: the close as it traded that day, and the share count known that day. The close is easy. The count is the work. US companies state it on the cover of every 10-Q and 10-K — "the number of shares outstanding of each of the issuer's classes of common stock, as of the latest practicable date" in the Form 10-Q instructions — so it changes four times a year and is published a few weeks after the date it describes. Every day in between carries the last count forward.
If a vendor's definition will do, Sharadar's DAILY table is that series already
built: market cap, enterprise value, P/E, P/B, P/S, EV/EBIT and EV/EBITDA per trading day, from
December 1998, in the Fundamentals and Bundle products. Its own definitions say what it did: market
cap is basic shares "sourced from the most recent SEC form 10 filing" times price times a share
factor, and P/E is market cap over net income to common. The free sample key covers the 30 Dow
stocks. If the definition has to be yours, build it from SEC EDGAR, below.
What the options are
Build it from the filings. SEC EDGAR's companyconcept endpoint returns
every value a company filed for one XBRL tag, each with the period it describes (end), the form and
the date it was filed (filed). dei:EntityCommonStockSharesOutstanding is the cover-page count;
us-gaap:NetIncomeLoss is the earnings. No key, no plan, ten requests a second, and every definition
in the result is one you chose. You still need daily prices, unadjusted — the count on the cover is
in that day's shares, and a split-adjusted close multiplied by it is wrong for every day before the
split. Tiingo returns raw close and a splitFactor on the same row, which is what
the script below uses.
Take a vendor's daily series. Sharadar as above, with its definitions
published per field. Financial Modeling Prep's
historical-market-capitalization takes a symbol, a from and a to and returns up to 5,000 rows a
call; its documentation does not say which share count it uses. Its P/E lives in the ratios and
key-metrics endpoints, one row per fiscal period, so a P/E between period ends means dividing the
market-cap series by earnings yourself. Tiingo's fundamentals/<ticker>/daily returns marketCap,
enterpriseVal, peRatio, pbRatio and trailingPEG1Y per day, each defined in its documentation
only as "the value of the field corresponding to" its own name; it is a paid add-on, and the free plan
sees three years of the Dow 30. EODHD's historical-market-cap is weekly, starts on
9 July 2021, covers NYSE and Nasdaq and nothing else, costs 10 API calls a request, and has no P/E
beside it.
Look it up without code. Stock Analysis has a market-cap page per stock that states its formula — stock price times shares outstanding — and names Nasdaq Data Link as the source. Without an account it charts the series from December 1998 and tabulates it at each year end back to 2002. The quarterly ratio table adds P/E and market cap at each quarter end, five years of them free. Getting either out as a file is a Pro download, at $79 a year and one a day.
Building it from EDGAR
The script takes the cover-page counts and quarterly net income from EDGAR and raw closes from
Tiingo, and returns a daily market cap and trailing P/E. It needs pandas 2.2 or later and
requests. The SEC requires a User-Agent on every request naming you and a contact address.
import pandas as pd
import requests
SEC = {"User-Agent": "Your Name you@example.com"} # the SEC requires a contact
TIINGO_KEY = "YOUR_API_KEY"
def sec_concept(cik, taxonomy, tag, unit):
"""Every value a company filed for one XBRL concept, as first reported."""
url = (f"https://data.sec.gov/api/xbrl/companyconcept/"
f"CIK{cik:010d}/{taxonomy}/{tag}.json")
resp = requests.get(url, headers=SEC, timeout=30)
resp.raise_for_status()
df = pd.DataFrame(resp.json()["units"][unit])
for col in ("start", "end", "filed"):
if col in df:
df[col] = pd.to_datetime(df[col])
period = ["start", "end"] if "start" in df else ["end"]
# a later filing repeats earlier values as comparatives, sometimes restated;
# keep the first filing of each period, which is what was known at the time
return df.sort_values("filed").drop_duplicates(period)
def tiingo_prices(ticker, start):
resp = requests.get(
f"https://api.tiingo.com/tiingo/daily/{ticker}/prices",
params={"startDate": start},
headers={"Authorization": f"Token {TIINGO_KEY}"},
timeout=30,
)
resp.raise_for_status()
px = pd.DataFrame(resp.json())[["date", "close", "splitFactor"]]
px["date"] = pd.to_datetime(px["date"]).dt.tz_localize(None)
return px.sort_values("date")
def ttm_net_income(cik):
ni = sec_concept(cik, "us-gaap", "NetIncomeLoss", "USD")
days = (ni["end"] - ni["start"]).dt.days
q = ni[days.between(80, 100)] # three-month facts from 10-Qs
fy = ni[days.between(350, 380)] # the 10-K reports only the year
q4 = []
for _, y in fy.iterrows():
inside = q[(q["start"] >= y["start"]) & (q["end"] <= y["end"])]
if len(inside) == 3:
q4.append({"end": y["end"], "filed": y["filed"],
"val": y["val"] - inside["val"].sum()})
q = pd.concat([q[["end", "filed", "val"]], pd.DataFrame(q4)])
q = q.sort_values("end").reset_index(drop=True)
q["ttm"] = q["val"].rolling(4).sum()
gap = (q["end"] - q["end"].shift(3)).dt.days
q.loc[~gap.between(250, 300), "ttm"] = None # four quarters, no holes
return q.dropna(subset=["ttm"]).sort_values("filed")[["filed", "ttm"]]
cik, ticker = 320193, "AAPL" # Apple
px = tiingo_prices(ticker, "2019-01-01")
shares = sec_concept(cik, "dei", "EntityCommonStockSharesOutstanding", "shares")
# A count is usable from the day it was filed, not from its as-of date.
daily = pd.merge_asof(px, shares[["filed", "end", "val"]],
left_on="date", right_on="filed", direction="backward")
daily = daily.dropna(subset=["val"])
# The count is as of `end`. A split after that date multiplies it.
cum = px.set_index("date")["splitFactor"].cumprod()
daily["shares"] = (daily["val"] * daily["date"].map(cum)
/ cum.asof(pd.DatetimeIndex(daily["end"])).to_numpy())
daily["market_cap"] = daily["close"] * daily["shares"]
# Trailing P/E as market cap over four quarters of net income, again by filing date.
daily = pd.merge_asof(daily, ttm_net_income(cik), left_on="date",
right_on="filed", direction="backward", suffixes=("", "_ni"))
daily["pe_ttm"] = (daily["market_cap"] / daily["ttm"]).where(daily["ttm"] > 0)
monthly = daily.set_index("date")[["market_cap", "pe_ttm"]].resample("ME").last()
print(monthly.tail(12))
Three choices in it are the whole method. The counts are joined on filed, not on end, so a
backtest on 20 July 2026 does not see a count the company published on 31 July; join on end instead
if you want the best hindsight figure rather than what was knowable. Each count is multiplied by the
splits between its end date and the day it is used, which is what keeps the series continuous
through a split. And the fourth quarter is the 10-K's year less the three 10-Q quarters inside it,
because the annual report states only the year.
Run on 9 October 2026 against EDGAR, with unadjusted Apple closes, the script gave a market cap of
$4,968,150.8 million for 8 October 2026 and $2,206,911.2 million for 31 August 2020, the same figures
to the decimal as Sharadar's DAILY table for those dates, and a P/E of 38.5 and 37.8 where Sharadar
shows 38.5 and 37.8. That is a check of the arithmetic, not a claim that either is the right
definition.
Where this breaks
More than one share class. The XBRL APIs aggregate only facts that, in the SEC's words, "apply to
the entire filing entity". Alphabet tags its cover count per class — Class A, B and C — so the
companyconcept call for EntityCommonStockSharesOutstanding has no file
for Alphabet at all, and the script fails on the first request. Berkshire Hathaway's entity-wide
count in the API ends with a 10-Q filed in May 2011. For either company you read the per-class counts
out of the filing and decide how to price a class with no listing; Sharadar carries a sharefactor
field for exactly this and names BRK.B in its definition. Why two screeners
disagree works through Alphabet's three classes.
A split between the count date and the filing. Apple's 10-Q filed on 31 July 2020 gives 4,275,634,000 shares as of 17 July. The four-for-one split came next: the unadjusted close went from $499.23 on 28 August to $129.04 on 31 August. The next count, 17,001,802,000 as of 16 October, was not filed until 30 October. A forward-fill without the split factor shows Apple worth a quarter of itself for two months. A split-adjusted price series has the opposite fault on every day before the split, which is the subject of why adjusted close differs.
Buybacks between filings. The count is a step function, and the company is not. Apple's cover count fell from 14,776,353,000 in October 2025 to 14,594,180,000 in July 2026, about 1.2% in nine months, and each step lands weeks after the date it describes. A daily series built this way is stale by up to a quarter plus the filing lag, and a vendor that estimates the count between filings will disagree with it by that much.
Restated earnings. EDGAR keeps every version. Apple's diluted EPS for the June 2020 quarter is
$2.58 in the 10-Q filed in July 2020 and $0.65 in the 10-K filed that October, after the split; the
frames endpoint returns the last-filed value, and a later restatement replaces the original there
too. Dividing a split-restated EPS into an unadjusted price from before the split gives a P/E four
times too high. The script keeps first-reported values and uses net income rather than EPS, which
avoids the per-share restatement but not an accounting one. What a dataset knew on the day is the
question point-in-time data answers; Sharadar's As-Reported dimensions do,
and its DAILY rows can still be recomputed later — the Apple rows for August 2020 carry a
lastupdated of 19 December 2021.
Which P/E. Trailing net income over market cap, price over reported EPS, price over diluted EPS, price over an adjusted figure the company presents (non-GAAP), or price over next year's estimate are five different numbers. On 9 October 2026 Stock Analysis showed Apple at a trailing 39.05 and a forward 36.96, and yfinance at 39.0 and 35.5. None of the APIs here stores the analyst estimate as it stood each day, so a historical forward P/E is not on this page; why consensus estimates differ explains why the estimate itself moves. A company with negative trailing earnings has no meaningful P/E, which is why the script blanks it.
Yahoo's share history. yfinance 1.7.0's get_shares_full reads an undocumented Yahoo time
series and defaults to the last 548 days. On 9 October 2026 it returned 62 rows for Apple, with two
different counts dated 11 April 2025 — 15.02 billion and 15.69 billion. Neither is on an Apple cover
page.
Foreign filers. A company filing a 20-F reports once a year, so its cover count changes once a year, and an ADR's price is per depositary share rather than per ordinary share.
If you outgrow this
The whole market rather than one ticker. EDGAR's frames endpoint returns one value per filer for
one concept and one period — CY2026Q2I for an instant — in a single call, and the nightly
companyfacts.zip holds every company's facts at once; both carry the same entity-wide limit and the
frames call carries last-filed values only. A universe also needs the delisted names, which is
survivorship-free data. SEC filings as
data covers the rest of EDGAR's surfaces.
A spreadsheet rather than a script. Fundamentals in a spreadsheet covers the add-ins that return ratios into cells; most of them return the current figure, so check for a date parameter before building a history on one. For prices alone in code, see replacing yfinance in a script.
The tools that do this
In the order this page recommends trying them. Paid placement does not affect this order.
SEC EDGAR
Cover-page share counts and quarterly net income, each stamped with its filing date. Free and keyless; you supply prices and carry counts forward yourself.
Every US filing since 1994, free and keyless — the limit is ten requests a second.
FreeFree tier
Sharadar
A DAILY table of market cap, EV and P/E back to December 1998, built from cover-page shares. Fundamentals from $19 a month, personal use only.
Point-in-time US fundamentals, EOD prices and 13F tables — now sold direct to individuals.
$9/moFree tier
Financial Modeling Prep
A historical-market-capitalization endpoint with from and to dates, up to 5,000 rows a call. P/E comes once per fiscal period, from ratios.
Financial statements, ratios and filings for 70,000+ securities across 60+ exchanges.
$22/moFree tier
Tiingo
Daily marketCap and peRatio from the fundamentals endpoint, undefined in the docs. A paid add-on on top of Power; the free plan sees the Dow 30 only.
End-of-day equity history back to 1962, plus crypto, forex and news, from $30 a month.
$30/moFree tier
EODHD
Weekly market cap from July 2021, NYSE and Nasdaq only, 10 API calls a request, on the Fundamentals or All-In-One plan.
End-of-day and fundamentals for 60+ exchanges worldwide, at a hobbyist price.
$19.99/moFree tier
Stock Analysis
No code. Year-end market cap free back to 2002, quarterly P/E for five years; downloads need Pro at $79 a year.
Five years of financials and a working screener, free and without an account.
$9.99/moFree tier
FAQ
Can yfinance give me historical market cap?
Not directly. The info dictionary's marketCap, trailingPE and forwardPE are today's values. Ticker.get_shares_full returns a share-count series from an undocumented Yahoo endpoint, 18 months by default, which you can multiply by unadjusted closes yourself, but on 9 October 2026 it returned two different Apple counts for the same day in April 2025, so check it against a filing before trusting it.
Why does my market cap differ from the one on a finance site?
Because both factors are choices. The share count may be the cover-page count, the weighted average behind EPS, or a vendor's own estimate; it may cover one share class or all of them; and it may be the count as filed then or as restated after a split. The price may be one listing's close. Two correct calculations can disagree by several percent on any company that buys back stock.
Why does the EDGAR API return nothing for Alphabet's shares outstanding?
Alphabet reports its cover-page count per share class, and the XBRL APIs only aggregate facts that apply to the entire filing entity. A count tagged to Class A, B or C is excluded, so the companyconcept call for EntityCommonStockSharesOutstanding has no file for Alphabet at all. Berkshire Hathaway's entity-wide count in the API stops in 2011 for the same reason.
Is a historical P/E trailing or forward?
Usually trailing, and the definition still varies. Sharadar's daily pe divides market cap by net income to common shareholders; its pe1 divides price by EPS. A forward P/E needs the analyst estimate as it stood on each date, which none of the sources on this page stores as a daily history.
Sources
- EDGAR Application Programming Interfaces — U.S. Securities and Exchange Commission, read
- Form 10-Q General Instructions — U.S. Securities and Exchange Commission, read
- Daily Fundamentals — Sharadar documentation — Sharadar, read
- Indicator Descriptions — Sharadar documentation — Sharadar, read
- Historical Market Cap API — Financial Modeling Prep, read
- Financial Ratios API — Financial Modeling Prep, read
- Fundamental Data API documentation — Tiingo, read
- End-of-Day (EOD) Stock Price API documentation — Tiingo, read
- Historical Market Capitalization API — EODHD, read
- Apple (AAPL) Market Cap — Stock Analysis, read
- yfinance/base.py — Ticker.get_shares_full — yfinance project, read
The catalogue next door
This page names a handful of cards. The rest of them are in Fundamental Data & Stock Research Platforms, each filled in against the same schema, with the fields to narrow it yourself.
Last updated . Corrected in place: this is a reference page, not a dated post.