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.

  1. 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

  2. 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

  3. 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

  4. 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

  5. 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

  6. 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

  1. EDGAR Application Programming Interfaces — U.S. Securities and Exchange Commission, read
  2. Form 10-Q General Instructions — U.S. Securities and Exchange Commission, read
  3. Daily Fundamentals — Sharadar documentation — Sharadar, read
  4. Indicator Descriptions — Sharadar documentation — Sharadar, read
  5. Historical Market Cap API — Financial Modeling Prep, read
  6. Financial Ratios API — Financial Modeling Prep, read
  7. Fundamental Data API documentation — Tiingo, read
  8. End-of-Day (EOD) Stock Price API documentation — Tiingo, read
  9. Historical Market Capitalization API — EODHD, read
  10. Apple (AAPL) Market Cap — Stock Analysis, read
  11. 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.

Report an error on this page

Quote the line and say what it should be.

The vendor’s page, filing or documentation that says otherwise.

Only if you want to hear back. Never published.

Read by a person. It fixes a fact and moves nothing else.