Finance APIs Series — Part 2 of 5

Fundamentals APIs — Financials, Filings & Company Data

Every source for income statements, balance sheets, SEC filings, institutional ownership, and insider transactions. From the 100% free SEC EDGAR to premium 13F trackers.

SEC EDGAR 13F Filings Free Sources Insider Trades
Finance APIs — The Ultimate Guide2/5
OverviewSEC EDGARFinancial Modeling PrepOwnership & InsidersAlternative DataComparisonCode ExamplesKey Takeaways
The Fundamentals Landscape

Why Fundamentals Data Matters

Price data tells you what happened. Fundamentals tell you why. Revenue growth, profit margins, debt levels, insider buying, institutional positioning — these are the building blocks of value investing, factor models, and every earnings-based trading strategy.

The good news: unlike real-time price data, most fundamental data is publicly available for free. The SEC mandates that every public company file standardized financial reports, and all of this data is accessible through EDGAR. The paid APIs add convenience — they parse, standardize, and deliver it through clean REST endpoints.

The Three Financial Statements

Every public company files three core financial statements:

Types of Fundamental Data

1

Financial Statements

Income, balance sheet, cash flow. Quarterly (10-Q) and annual (10-K). The core of fundamental analysis.

2

SEC Filings

10-K, 10-Q, 8-K, proxy statements, registration statements. Full text + structured XBRL data.

3

Institutional Ownership

13F filings: what hedge funds and institutions own. Updated quarterly, 45-day delay.

4

Insider Transactions

Form 4 filings: CEO/CFO/director buys and sells. Strong signal — insiders buy for one reason only.

Government Sources

SEC EDGAR — The Ultimate Free Source

SEC EDGAR (Electronic Data Gathering, Analysis, and Retrieval)

100% Free No API Key Government Source

EDGAR is the SEC's filing system. Every document that every public company has ever filed with the SEC lives here — 10-Ks, 10-Qs, 8-Ks, proxy statements, insider transaction forms, 13F filings. It's the authoritative source of truth for all US public company data. The new EDGAR API provides structured JSON access to company facts, filings, and XBRL data.

Pros

  • 100% free, unlimited access
  • Authoritative (it IS the source)
  • Full-text search across all filings
  • XBRL structured data (machine-readable)
  • New JSON API (company facts endpoint)
  • Historical data back to 1993

Cons

  • Rate limit: 10 requests/second
  • Data is raw — requires parsing
  • XBRL taxonomy varies by company
  • No standardized financials
  • UI is from the 1990s

The New EDGAR API — Company Facts

In 2022, the SEC launched a modern JSON API for structured company data. The /api/xbrl/companyfacts/ endpoint provides every XBRL fact ever filed by a company — revenue, net income, EPS, assets, and hundreds more metrics, all in clean JSON.

Python
import requests
import pandas as pd

# EDGAR requires a User-Agent header with your email
headers = {"User-Agent": "YourApp/1.0 ([email protected])"}

# Get all XBRL facts for Apple (CIK = 0000320193)
url = "https://data.sec.gov/api/xbrl/companyfacts/CIK0000320193.json"
data = requests.get(url, headers=headers).json()

# Extract revenue (us-gaap:Revenues or RevenueFromContractWithCustomerExcludingAssessedTax)
revenue_facts = data["facts"]["us-gaap"]["RevenueFromContractWithCustomerExcludingAssessedTax"]
units = revenue_facts["units"]["USD"]

# Filter for 10-K annual filings only
annual = [u for u in units if u["form"] == "10-K"]
for a in annual[-5:]:
    print(f"  {a['end']}: ${a['val']/1e9:.1f}B")

# Output:
# 2021-09-25: $365.8B
# 2022-09-24: $394.3B
# 2023-09-30: $383.3B
# 2024-09-28: $391.0B
# 2025-09-27: $410.2B

Full-Text Search (EFTS)

Python
# Search all filings for a keyword
search_url = "https://efts.sec.gov/LATEST/search-index?q=\"artificial intelligence\"&dateRange=custom&startdt=2025-01-01&enddt=2026-03-06&forms=10-K"
results = requests.get(search_url, headers=headers).json()

print(f"Found {results['hits']['total']['value']} 10-K filings mentioning 'artificial intelligence'")
for hit in results["hits"]["hits"][:5]:
    print(f"  {hit['_source']['display_names'][0]} — {hit['_source']['file_date']}")

Insider Transactions (Form 4)

Python
# Get recent insider filings for Apple
filings_url = "https://data.sec.gov/submissions/CIK0000320193.json"
company = requests.get(filings_url, headers=headers).json()

# Filter for Form 4 (insider transactions)
recent = company["filings"]["recent"]
form4_indices = [i for i, f in enumerate(recent["form"]) if f == "4"]

for i in form4_indices[:5]:
    print(f"  {recent['filingDate'][i]} — {recent['primaryDocument'][i]}")

CIK Numbers

EDGAR uses CIK (Central Index Key) numbers, not ticker symbols. To look up a CIK: https://www.sec.gov/cgi-bin/browse-edgar?action=getcompany&company=apple&CIK=&type=&dateb=&owner=include&count=10&search_text=&action=getcompany. Or use the company tickers JSON: https://www.sec.gov/files/company_tickers.json which maps every ticker to its CIK.

Standardized Financials

Standardized Financial Statement APIs

Financial Modeling Prep (FMP)

Freemium 250 req/day Free $15-65/mo

FMP takes the raw SEC data and standardizes it into clean, consistent JSON. Their free tier gives you 250 API calls per day — enough to build a serious screener. They cover income statements, balance sheets, cash flows, ratios, DCF models, and more for 13,000+ stocks.

Pros

  • 250 req/day free (generous)
  • Standardized financials (consistent keys)
  • Financial ratios pre-calculated
  • DCF models built in
  • 13,000+ stocks globally
  • Good documentation

Cons

  • Historical depth limited on free
  • Occasional data quality issues
  • Batch endpoints paid only
Python
import requests

FMP_KEY = "YOUR_FMP_KEY"
base = "https://financialmodelingprep.com/api/v3"

# Income Statement (quarterly)
income = requests.get(f"{base}/income-statement/AAPL?period=quarter&limit=8&apikey={FMP_KEY}").json()
for q in income[:4]:
    print(f"  {q['date']} — Revenue: ${q['revenue']/1e9:.1f}B, Net Income: ${q['netIncome']/1e9:.1f}B")

# Balance Sheet (annual)
balance = requests.get(f"{base}/balance-sheet-statement/AAPL?limit=5&apikey={FMP_KEY}").json()

# Key Financial Ratios
ratios = requests.get(f"{base}/ratios/AAPL?limit=5&apikey={FMP_KEY}").json()
for r in ratios[:3]:
    print(f"  {r['date']} — P/E: {r['priceEarningsRatio']:.1f}, ROE: {r['returnOnEquity']:.1%}")

# DCF (Discounted Cash Flow) valuation
dcf = requests.get(f"{base}/discounted-cash-flow/AAPL?apikey={FMP_KEY}").json()
print(f"  DCF value: ${dcf[0]['dcf']:.2f} vs Market: ${dcf[0]['Stock Price']:.2f}")

SimFin

Free Bulk Download API $10/mo

SimFin takes a unique approach: they offer free bulk CSV downloads of standardized financials for 5,000+ US stocks. No API key needed — just download the files. They also have a Python library (simfin) that handles the downloading and caching automatically. The paid API tier adds real-time access and more history.

Pros

  • Free bulk CSV downloads
  • Excellent Python library
  • Truly standardized (same columns for all companies)
  • Great for backtesting fundamental strategies

Cons

  • US stocks only
  • Bulk data updated monthly (not real-time)
  • ~5,000 stocks (not comprehensive)
Python
import simfin as sf

# Set your API key (free) and data directory
sf.set_api_key("free")
sf.set_data_dir("~/simfin_data/")

# Download and cache income statements (annual)
df_income = sf.load_income(variant="annual", market="us")

# Filter for Apple
aapl = df_income.loc["AAPL"]
print(aapl[["Revenue", "Net Income", "Shares (Diluted)"]].tail())

# Download balance sheets
df_balance = sf.load_balance(variant="quarterly", market="us")

# Download cash flow statements
df_cashflow = sf.load_cashflow(variant="annual", market="us")

# Calculate Free Cash Flow for all companies
df_cashflow["FCF"] = df_cashflow["Net Cash from Operating Activities"] + df_cashflow["Net Cash from Investing Activities"]

Macrotrends

Free (Web)

Macrotrends.net isn't an API — it's a website with 15+ years of historical fundamentals for every major stock. The site is scraping-friendly (simple HTML tables) and provides data that would cost $100+/month from a commercial API: revenue, EPS, margins, P/E ratios, book value, and more. Many quantitative traders use Macrotrends as their historical fundamental data source.

Institutional Ownership & Insider Transactions

Who Owns What — 13F & Insider Data

Fintel

$40-100/mo

Fintel is the premium source for institutional ownership data. They parse every 13F filing and provide searchable, sortable data on what hedge funds, mutual funds, and institutions own. They also track short interest, insider transactions, and unusual institutional activity. Their "Insider Score" aggregates insider buying signals.

Pros

  • Most comprehensive 13F database
  • Short interest data (hard to find free)
  • Insider buying/selling scores
  • Institutional activity alerts

Cons

  • No free tier
  • API access on higher plans only
  • 13F data is inherently 45-day delayed

WhaleWisdom

Freemium $15-50/mo

WhaleWisdom focuses specifically on 13F tracking. Their free tier lets you view the top 50 holders of any stock and the latest portfolio of any institution. The paid tiers add historical comparisons, portfolio change alerts, and the ability to clone any fund's portfolio. Their "WhaleScore" ranks stocks by hedge fund consensus.

Pros

  • Free tier for basic 13F lookup
  • Portfolio cloning feature
  • WhaleScore ranking system
  • Historical portfolio evolution

Cons

  • No API on free tier
  • US-only
  • 45-day filing delay (inherent to 13F)

The 13F Delay Problem

13F filings are due 45 days after the end of each quarter. This means that when you see a Q4 filing in mid-February, the data reflects positions as of December 31st — 45 days stale. Hedge funds know this and sometimes use "13F window dressing" to manipulate their reported positions. Always treat 13F data as directional (trend of positions), not as a real-time snapshot.

Alternative Data Sources

Alternative & Unconventional Data

QuiverQuant

Free Open Source

QuiverQuant is an incredible free resource that tracks alternative data no one else aggregates: Congressional stock trades (STOCK Act disclosures), lobbying expenditures, government contracts, patent filings, and Wikipedia page views. The "Congress Trading" dataset alone has generated multiple viral research papers showing that members of Congress consistently outperform the S&P 500.

Pros

  • Completely free with API access
  • Congressional trades (unique dataset)
  • Government contracts data
  • Patent filings by company
  • Wikipedia page views as proxy for interest

Cons

  • Congressional data has 45-day delay
  • US-only
  • Smaller dataset than commercial providers
Python
import requests

# QuiverQuant — Congressional Trading
headers = {"Authorization": f"Bearer {QUIVER_KEY}"}

# Recent congressional trades
congress = requests.get("https://api.quiverquant.com/beta/historical/congresstrading/AAPL", headers=headers).json()
for t in congress[:5]:
    print(f"  {t['TransactionDate']} — {t['Representative']} ({t['Party']}) — {t['Transaction']} ${t['Range']}")

# Government contracts
contracts = requests.get("https://api.quiverquant.com/beta/historical/govcontracts/MSFT", headers=headers).json()

# Patent filings
patents = requests.get("https://api.quiverquant.com/beta/historical/patents/GOOGL", headers=headers).json()

SEC API (New EDGAR API)

100% Free JSON Format

The SEC's newest API provides structured company data in clean JSON format. The /submissions/ endpoint gives you filing history, and the /companyfacts/ endpoint provides standardized XBRL data. Unlike the old EDGAR full-text search, this API returns machine-readable data ready for analysis.

Python
import requests

headers = {"User-Agent": "YourApp/1.0 ([email protected])"}

# Company search by ticker -> CIK mapping
tickers = requests.get("https://www.sec.gov/files/company_tickers.json", headers=headers).json()
# Find CIK for any ticker
ticker_map = {v["ticker"]: v["cik_str"] for v in tickers.values()}
aapl_cik = ticker_map["AAPL"]  # 320193

# Get all submissions/filings
submissions = requests.get(f"https://data.sec.gov/submissions/CIK{str(aapl_cik).zfill(10)}.json", headers=headers).json()
print(f"Company: {submissions['name']}")
print(f"SIC: {submissions['sic']} — {submissions['sicDescription']}")

# Recent filings
recent = submissions["filings"]["recent"]
for i in range(5):
    print(f"  {recent['filingDate'][i]} — {recent['form'][i]} — {recent['primaryDocument'][i]}")
Comparison

Fundamentals API Comparison

Source Price Financials 13F / Ownership Insiders Alt Data API Quality
SEC EDGAR Free Raw XBRL Yes (raw) Yes (Form 4) No Good (new API)
Financial Modeling Prep Free / $15+ Standardized Limited Limited No Good
SimFin Free / $10 Standardized No No No Excellent (Python)
Fintel $40+ No Best-in-class Yes + Score Short Interest Good
WhaleWisdom Free / $15+ No Excellent Limited WhaleScore Web-only free
QuiverQuant Free No Limited No Congress, Patents Good
Macrotrends Free (web) 15+ years No No No Scraping only

Data Coverage by Metric

Decision Matrix

Use Case Best Choice Why
Building a stock screener FMP (free tier) 250 req/day, standardized financials + ratios + DCF
Backtesting value strategies SimFin (free bulk) Download all financials as CSV, consistent schema
Tracking smart money Fintel ($40/mo) Best 13F database + insider scores + short interest
Following Congress trades QuiverQuant (free) Only source that aggregates STOCK Act disclosures
Full-text filing search SEC EDGAR (free) EFTS search across all filings, mentions of keywords
Quick fundamental lookup Macrotrends (free) No API key, 15+ years history, simple HTML tables
Building a Financial Database

Building a Local Financial Database

The smartest approach is to download fundamental data once and store it locally. Here's how to build a SQLite-based financial database using free sources:

Python
import sqlite3
import requests
import json
from datetime import datetime

class FinancialDB:
    """Local SQLite database for fundamental data."""

    def __init__(self, db_path="financials.db"):
        self.conn = sqlite3.connect(db_path)
        self._create_tables()

    def _create_tables(self):
        self.conn.executescript("""
        CREATE TABLE IF NOT EXISTS income_statements (
            ticker TEXT, date TEXT, period TEXT,
            revenue REAL, gross_profit REAL, operating_income REAL,
            net_income REAL, eps REAL, shares_outstanding REAL,
            PRIMARY KEY (ticker, date, period)
        );
        CREATE TABLE IF NOT EXISTS balance_sheets (
            ticker TEXT, date TEXT,
            total_assets REAL, total_liabilities REAL,
            total_equity REAL, cash REAL, total_debt REAL,
            PRIMARY KEY (ticker, date)
        );
        CREATE TABLE IF NOT EXISTS insider_trades (
            ticker TEXT, date TEXT, insider_name TEXT,
            transaction_type TEXT, shares REAL, price REAL,
            value REAL
        );
        """)

    def load_from_fmp(self, ticker, api_key):
        """Load financials from Financial Modeling Prep."""
        base = "https://financialmodelingprep.com/api/v3"

        # Income statements
        income = requests.get(
            f"{base}/income-statement/{ticker}?limit=20&apikey={api_key}"
        ).json()

        for q in income:
            self.conn.execute(
                "INSERT OR REPLACE INTO income_statements VALUES (?,?,?,?,?,?,?,?,?)",
                (ticker, q["date"], q["period"], q["revenue"],
                 q["grossProfit"], q["operatingIncome"],
                 q["netIncome"], q["eps"], q["weightedAverageShsOut"])
            )
        self.conn.commit()

    def load_from_edgar(self, ticker, cik):
        """Load from SEC EDGAR Company Facts (free, no key)."""
        headers = {"User-Agent": "FinDB/1.0 ([email protected])"}
        url = f"https://data.sec.gov/api/xbrl/companyfacts/CIK{str(cik).zfill(10)}.json"
        data = requests.get(url, headers=headers).json()

        # Extract revenue
        for key in ["Revenues", "RevenueFromContractWithCustomerExcludingAssessedTax"]:
            if key in data["facts"].get("us-gaap", {}):
                units = data["facts"]["us-gaap"][key]["units"]["USD"]
                for u in units:
                    if u["form"] in ("10-K", "10-Q"):
                        self.conn.execute(
                            "INSERT OR REPLACE INTO income_statements (ticker, date, period, revenue) VALUES (?,?,?,?)",
                            (ticker, u["end"], "FY" if u["form"] == "10-K" else "Q", u["val"])
                        )
                break
        self.conn.commit()

    def query(self, sql):
        return self.conn.execute(sql).fetchall()

# Usage
db = FinancialDB()
db.load_from_fmp("AAPL", "YOUR_FMP_KEY")
db.load_from_fmp("MSFT", "YOUR_FMP_KEY")

# Compare revenue growth
results = db.query("""
    SELECT ticker, date, revenue/1e9 as revenue_bn
    FROM income_statements
    WHERE period = 'FY' AND ticker IN ('AAPL', 'MSFT')
    ORDER BY ticker, date DESC LIMIT 10
""")
for r in results:
    print(f"  {r[0]} — {r[1]} — ${r[2]:.1f}B")
Key Takeaways

Key Takeaways

What You Should Remember

How do I handle companies that change their XBRL taxonomy?

This is the biggest pain point with EDGAR data. Revenue might be filed under "Revenues", "RevenueFromContractWithCustomerExcludingAssessedTax", or "SalesRevenueNet" depending on the company and the year. The solution is to maintain a mapping table of equivalent XBRL tags. SimFin and FMP handle this for you — they've already built the mapping. If you're using EDGAR directly, start with the most common tags and add fallbacks.

Which API should I use for international fundamentals?

Most free sources are US-only. For international fundamentals: FMP covers 13,000+ stocks globally including European and Asian markets. EODHD ($20/mo) provides fundamentals for 60+ exchanges. For EU specifically, the European Single Electronic Format (ESEF) is the equivalent of EDGAR XBRL, but it's much harder to work with.

Next in Series — Part 3
Sentiment & Social APIs — StockTwits, Reddit, Twitter
Finance APIs — The Ultimate Guide2/5