Financial Data
Done Right

Financial data API for US equities.
Spend less time cleaning, more time researching.

Free API Python client MCP server

Get API key Docs

Key Features.

Coverage & quality
81M+
Daily prices
47K+
Total entities
17K+
Delisted entities
50+ yrs
Of history
150+
Fundamental fields
40+
Pre-computed metrics
Survivorship-bias free
Full price history for every company that ever traded — including the ones that failed.
Entity resolution
Recycled tickers mapped to the right entity, so history never blends two companies.
Stable prices
Split-adjusted to the academic standard. The same date returns the same number, every pull.
Insider & index data
Form 4 insider transactions and point-in-time index membership.
GICS classification
Every entity tagged to its GICS sector and industry, standardized.
Industry-aware fields
Banks get bank fields, REITs get REIT fields. If a field doesn't apply, it's null.
Standardized columns
Same field names, same units, every company — straight into pandas.
Plot-ready DataFrames
Tidy format, proper dtypes, parsed dates. No reshaping between API and chart.
AI-native over MCP
Connect any LLM agent over MCP — your AI gets the same clean data you do.

Endpoints.

Python · REST · MCP
EndpointGrainDescriptionCoverage
/pricesTicker-dateOHLCV, returns, splits, and dividends across history.30+ yrs
/fundamentalsEntity-periodStandardized filing statements and segments by period.50+ yrs
/metricsEntity-periodRatios, margins, growth, and valuation by period.30+ yrs
/insidersTransactionInsider transactions with roles, values, and ownership.30+ yrs
/resolveTicker spellTicker history, entities, classifications, and index membership.50+ yrs
/indexIndex-dateHistorical index membership with additions and removals.50+ yrs
/searchEntityCompany lookup by name, ticker, sector, or code.Current

Examples.

Multi-endpoint · Python
01Point-in-time index screen
1 / 5
Request
sp500 = xfl.index("sp500", as_of="2020-01-01")

metrics = xfl.metrics(
    sp500["ticker"].dropna().head(100),
    period_type="annual",
    fields=["market_cap", "pe_ratio", "roe"],
)

metrics.sort_values("roe", ascending=False).head()
Response
ticker  period_end   market_cap   pe_ratio    roe
AAPL    2019-09-28    1,287,000   22.4      0.559
MSFT    2019-06-30    1,203,000   27.8      0.383
V       2019-09-30      412,800   34.6      0.349
Request
xfl.resolve("C")["data"]["C"]["entities"]

prices = xfl.prices(
    "C",
    start="2024-01-01",
    fields=["close", "return_daily"],
).tail()
Response
name                       valid_from   valid_to
Citigroup Inc.             1998-10-08   null
Travelers Companies        1993-03-22   1998-10-07

date         ticker   close   return_daily
2024-12-24   C        70.39        0.0041
2024-12-26   C        70.52        0.0018
2024-12-27   C        70.21       -0.0044
Request
trades = xfl.insiders(
    "NVDA",
    period="1y",
    transaction_type="open_market_sell",
    fields=["insider_name", "insider_role",
            "shares", "transaction_value"],
)

valuation = xfl.metrics(
    "NVDA", fields=["market_cap", "pe_ratio", "roe"],
).tail(1)
Response
insider_name    role       shares    transaction_value
Jensen Huang    CEO        120,000          14,820,000
Mark Stevens    Director    40,000           4,930,000

ticker  market_cap   pe_ratio   roe
NVDA     3,240,000   41.8     0.887
Request
tech = xfl.search(gics_sector="Information Technology", limit=100)

screen = xfl.metrics(
    tech["ticker"],
    fields=["market_cap", "pe_ratio"],
)

screen = screen.merge(
    tech[["ticker", "gics_sector"]],
    on="ticker",
)

screen.query("market_cap > 10000") \
      .sort_values("market_cap", ascending=False) \
      .head()
Response
ticker  gics_sector             market_cap  pe_ratio
NVDA    Information Technology   3,240,000      41.8
MSFT    Information Technology   3,110,000      35.6
AAPL    Information Technology   2,980,000      28.4
AVGO    Information Technology     812,600      34.2
ORCL    Information Technology     468,900      29.7
Blog
Warmed up?

Factor backtests, survivorship-bias studies, insider-signal research, and runnable notebooks live on the blog.

Read in-depth examples
LIVE · RESOLVE

Try it.

> to resolve
GET /v1/resolve/
200 OK
DOCS

xfinlink documentation.

Seven endpoints. Install, query, done. Everything you need to integrate xfinlink into a research pipeline, trading system, or weekend project.

Quick start

Install the package, set your API key, and make your first query in under a minute.

terminal
pip install xfinlink

Create a free account to get your API key, then:

quickstart.py
import xfinlink as xfl

# Set your API key — get a free key at https://xfinlink.com/signup
xfl.set_api_key("YOUR_API_KEY")

# Historical prices
prices = xfl.prices("AAPL", start="2024-01-01")

# Financial statements
fundamentals = xfl.fundamentals("AAPL", period_type="annual")

# Pre-computed metrics (P/E, ROE, margins, ...)
metrics = xfl.metrics("AAPL", fields=["pe_ratio", "roe"])

# Index constituents
sp500 = xfl.index("sp500")

# Search for entities
results = xfl.search(q="apple")

# Entity resolution (handles ticker recycling)
entity = xfl.resolve("GM")

Use an API key via the X-API-Key header for full access (the Python client handles this automatically).

Authentication

Most API requests require an API key. Create a free account to get yours. Search and resolve allow restricted unauthenticated calls; use a key for normal limits and full detail.

Pass the key via the X-API-Key header:

curl
curl -H "X-API-Key: your-api-key" \
  https://api.xfinlink.com/v1/prices/AAPL?start=2024-01-01

The Python client handles auth automatically:

auth.py
import xfinlink as xfl

# Set your API key — get a free key at https://xfinlink.com/signup
xfl.set_api_key("YOUR_API_KEY")

Your API key is shown on the dashboard after sign-up. You can regenerate it at any time.

/v1/prices/{ticker}

Daily OHLCV prices with split adjustment, total returns, and dividend/split event data. Coverage back to 1996 for every US-listed stock and ETF.

paramtypedefaultdescription
tickerstr | list[str]requiredTicker(s) to fetch.
startstr (ISO date)optionalEarliest date, inclusive.
endstr (ISO date)optionalLatest date, inclusive. Defaults to today.
fieldslist[str]optionalSubset of columns. Defaults to 11 fields; market_cap is opt-in.
adjuststr"split"Adjustment mode: split (default) or none.

Request:

request.py
df = xfl.prices("AAPL", start="2024-01-01", fields=["close", "volume"])

Response:

stdout
  date        ticker   close      volume
  2024-01-02  AAPL     185.64  45,123,456
  2024-01-03  AAPL     184.25  42,889,012
  2024-01-04  AAPL     181.91  49,301,234
  ...

/v1/fundamentals/{ticker}

Reported financials — income statement, balance sheet, cash flow — back to 1950 for most listed US companies.

paramtypedefaultdescription
tickerstr | list[str]requiredTicker(s) to fetch.
period_typestr"all""annual" | "quarterly" | "all"
fieldslist[str]optionalSubset of 147 available fields.
includestroptional"segments" — adds revenue breakdowns by geography, product, and business segment.
segment_membersstr"primary""primary" or "all". Primary returns segments summing to revenue; all includes subtotals.
startstr (ISO date)optionalEarliest period_end.
endstr (ISO date)optionalLatest period_end.
fiscal_yearintoptionalExact-match filter on fiscal_year.
period_endstr (ISO date)optionalExact-match filter on period_end.
request.py
df = xfl.fundamentals("AAPL", period_type="annual",
                  fields=["revenue", "net_income"])

/v1/resolve/{ticker}

Full entity history for a ticker. Every company that ever used this ticker, with validity dates, classifications, and index membership.

paramtypedefaultdescription
tickerstrrequiredTicker symbol or comma-separated tickers (up to 10).
includestroptionalindex, classifications
request.py
info = xfl.resolve("GM")
for entity in info["data"]["GM"]["entities"]:
    print(entity["name"], entity["ticker_valid_from"], entity["ticker_valid_to"])

/v1/index/{index_name}

Current and historical index constituents. Available indices: sp500, ndx100, djia, russell2000.

paramtypedefaultdescription
index_namestrsp500Index name (path parameter).
as_ofstr (ISO date)todayPoint-in-time membership date.
limitint500Results per page (max 1000).
offsetint0Pagination offset.
request.py
df = xfl.index("sp500")
df = xfl.index("sp500", as_of="2020-01-01")

/v1/insiders/{ticker} Pro

Insider transactions from SEC Form 3, 4, and 5 filings. One row per transaction. Shares and prices are split-adjusted. Coverage 1986 to present. Pro tier only; free-tier keys receive 402.

paramtypedefaultdescription
tickerstrrequiredTicker or comma-separated tickers (up to 100).
startstr (ISO date)1y agoEarliest transaction_date.
endstr (ISO date)optionalLatest transaction_date.
transaction_typestroptionalDecoded type, e.g. open_market_buy, open_market_sell, grant_or_award, option_exercise, tax_withholding, gift. Comma-separated for multiple.
acquisition_or_dispositionstroptional"A" or "D".
ownership_typestroptional"direct" or "indirect".
form_typestroptional"3", "4", "5" (or amended e.g. "4/A").
insider_rolestroptionalSubstring match on role (CEO, CFO, Director, etc.).
insider_namestroptionalSubstring match on name.
min_valuenumoptionalMinimum transaction_value in dollars.
include_amendmentsboolfalseInclude amendment rows.
limitint1000Rows per page, max 5000.

Fields returned: entity_id, ticker, entity_name, transaction_date, filing_date, form_type, insider_name, insider_role, insider_role_other, transaction_code, transaction_type, acquisition_or_disposition, shares, shares_held_after, transaction_price, transaction_value, ownership_type, is_amendment, document_id, sequence_in_filing, data_quality.

request.py
df = xfl.insiders("TSLA", transaction_type="open_market_sell", insider_role="CEO")

/v1/metrics/{ticker}

37 pre-computed financial metrics derived from prices and fundamentals. All ratios as decimals (0.46 = 46%). Market cap and enterprise value in millions USD. Per-share values in dollars.

paramtypedefaultdescription
tickerstrrequiredTicker or comma-separated tickers.
period_typestrannualannual or quarterly.
fieldsstrallComma-separated metric names.
startstr (ISO date)optionalStart date for period_end.
endstr (ISO date)optionalEnd date for period_end.
limitint20Rows per page.

Valuation: market_cap, market_cap_diluted, enterprise_value, pe_ratio, ps_ratio, pb_ratio, price_to_cash_flow, ev_ebitda, earnings_yield, dividend_yield

Profitability: gross_margin, operating_margin, net_margin, ebitda_margin, roe, roa, roic

Leverage: debt_to_equity, debt_to_assets, long_term_debt_to_equity, long_term_debt_to_assets, current_ratio, quick_ratio, interest_coverage

Efficiency: asset_turnover, inventory_turnover

Per-share ($/share): revenue_per_share, book_value_per_share, tangible_book_value_per_share, cash_per_share, debt_per_share, ocf_per_share, fcf_per_share, ebit_per_share, ebitda_per_share, capex_per_share, working_capital_per_share

request.py
df = xfl.metrics("AAPL", fields=["pe_ratio", "roe", "market_cap"])

Field reference

230+ data fields across seven endpoints. See the Explorer on the home page for live values.

• /v1/fundamentals — 147 fields

INCOME STATEMENT (38)

revenuecost_of_revenuecost_of_goods_soldgross_profitresearch_and_developmentselling_general_adminselling_and_marketinggeneral_and_adminstock_based_compensationdepreciation_amortizationdepreciationamortization_intangiblesdepletionrestructuring_chargesimpairment_chargesprovision_for_credit_lossesgain_loss_on_sale_of_assetsother_operating_expensesoperating_expenses_totaloperating_incomeinterest_expenseinterest_incomeincome_from_equity_method_investmentsother_non_operating_incomepretax_incomeincome_tax_expenseincome_from_discontinued_operationsnet_incomeminority_interest_incomenet_income_attributable_to_parentpreferred_dividendsnet_income_available_to_commoncomprehensive_incomeebitebitdaeps_basiceps_diluteddividends_per_share

BALANCE SHEET — ASSETS (22)

cash_and_equivalentsrestricted_cashmarketable_securitiesshort_term_investmentscash_and_short_term_investmentsaccounts_receivablecontract_assetsinventoryprepaid_expensesother_current_assetscurrent_assets_totalproperty_plant_equipment_grossaccumulated_depreciationproperty_plant_equipment_netoperating_lease_assetsfinance_lease_assetslong_term_investmentsequity_method_investmentsgoodwillintangible_assetsdeferred_tax_assetsother_noncurrent_assetstotal_assets

BALANCE SHEET — LIABILITIES (19)

accounts_payableshort_term_debtcurrent_portion_long_term_debtoperating_lease_liabilities_currentfinance_lease_liabilities_currentdeferred_revenue_currentaccrued_liabilitiesother_current_liabilitiescurrent_liabilities_totallong_term_debtoperating_lease_liabilities_noncurrentfinance_lease_liabilities_noncurrentdeferred_revenue_noncurrentdeferred_tax_liabilitiespension_liabilitiesinsurance_loss_reservesother_noncurrent_liabilitiestotal_liabilitiestotal_debt

BALANCE SHEET — EQUITY (10)

common_stock_valuepreferred_equityadditional_paid_in_capitalretained_earningstreasury_stockaccumulated_other_comprehensive_incometemporary_equitytotal_equityminority_interesttotal_equity_including_minority

CASH FLOW (33)

net_income_cfdepreciation_amortization_cfstock_based_compensation_cfdeferred_income_taxeschange_in_accounts_receivablechange_in_inventorychange_in_accounts_payablechange_in_working_capitalother_operating_activitiesoperating_cash_flowcapital_expendituresacquisitions_netproceeds_from_divestiturespurchases_of_investmentssales_of_investmentsother_investing_activitiesinvesting_cash_flowlong_term_debt_issuancelong_term_debt_repaymentshort_term_debt_proceedsshort_term_debt_repaymentsfinance_lease_principal_paymentsshare_issuanceshare_repurchasesdividends_paiddividends_paid_commonother_financing_activitiesfinancing_cash_flowcash_taxes_paidcash_interest_paideffect_of_exchange_rate_on_cashnet_change_in_cashfree_cash_flow

SHARES & CLASSIFICATION (7)

shares_outstandingcommon_shares_issuedweighted_avg_shares_basicweighted_avg_shares_dilutedtreasury_sharessicnaics

INDUSTRY — BANKING (9) • INSURANCE (5) • REIT (3)

bank_interest_incomebank_interest_expensebank_net_interest_incomebank_noninterest_incomebank_noninterest_expensebank_loans_netbank_depositsbank_allowance_for_credit_lossesbank_net_charge_offsins_premiums_earnedins_losses_incurredins_underwriting_expenseins_net_investment_incomeins_policy_benefitsreit_rental_revenuereit_fforeit_affo
• /v1/prices — 12 fields
openhighlowcloseadj_closevolumereturn_dailyshares_outstandingexchange_codesplit_ratiodividendmarket_cap
• /v1/metrics — 37 fields
market_capmarket_cap_dilutedenterprise_valuepe_ratiops_ratiopb_ratioprice_to_cash_flowev_ebitdaearnings_yielddividend_yieldgross_marginoperating_marginnet_marginebitda_marginroeroaroicdebt_to_equitydebt_to_assetslong_term_debt_to_equitylong_term_debt_to_assetscurrent_ratioquick_ratiointerest_coverageasset_turnoverinventory_turnoverrevenue_per_sharebook_value_per_sharetangible_book_value_per_sharecash_per_sharedebt_per_shareocf_per_sharefcf_per_shareebit_per_shareebitda_per_sharecapex_per_shareworking_capital_per_share
• /v1/resolve — 15 fields
entity_idnameentity_typecountryfigicikticker_valid_fromticker_valid_tosic_codenaics_codegics_sectorgics_groupgics_industrygics_subindustryindex_membership
• /v1/insiders — 21 fields (Pro)
entity_identity_nametickertransaction_datefiling_dateform_typeinsider_nameinsider_roleinsider_role_othertransaction_codetransaction_typeacquisition_or_dispositionsharesshares_held_aftertransaction_pricetransaction_valueownership_typeis_amendmentdocument_idsequence_in_filingdata_quality

Understanding price fields

xfinlink follows the academic convention for price data. This matters for backtesting and returns analysis.

fielddescription
closeRaw unadjusted closing price — what actually traded on the exchange that day.
adj_closeSplit-only adjusted close. Adjusted for stock splits but NOT for dividends. Comparable across split events.
return_dailyTotal daily return including dividends. Use this for performance analysis and backtesting.
dividendCash dividend amount on ex-date. Zero on non-dividend days.
split_ratioSplit ratio on split date (e.g., 4.0 for a 4:1 split). 1.0 on non-split days.

Adjustment direction: adj_close is split-only backward-adjusted to the current post-split share basis. To derive a forward-adjusted series from an anchor date, request raw close plus split_ratio, then compound split ratios forward.

forward_adjusted.py
import xfinlink as xfl

df = xfl.prices("AAPL", start="2020-08-27", end="2020-09-02", fields=["close", "split_ratio"], adjust="none")
df = df.sort_values("date")

factor = df["split_ratio"].fillna(1.0).cumprod()
df["forward_adj_close"] = df["close"] * factor
stdout
date        ticker  close      split_ratio  forward_adj_close
2020-08-27  AAPL    500.04001  NaN          500.04001
2020-08-28  AAPL    499.23001  NaN          499.23001
2020-08-31  AAPL    129.03999  4.0          516.15996
2020-09-01  AAPL    134.17999  NaN          536.71996
2020-09-02  AAPL    131.39999  NaN          525.59996

Why no dividend-adjusted prices? Dividend-adjusted prices retroactively change every time a company pays a dividend, making historical values unstable. xfinlink stores stable values (split-only adjusted) plus total returns, which is the academic standard for quantitative research.

How to compute dividend-adjusted prices client-side:

div_adjusted.py
import xfinlink as xfl

df = xfl.prices("AAPL", start="2020-01-01", fields=["adj_close", "return_daily"])

# Build cumulative total return index
df["total_return_index"] = (1 + df["return_daily"]).cumprod()

# Derive dividend-adjusted price series
df["div_adj_close"] = df["adj_close"].iloc[-1] * df["total_return_index"] / df["total_return_index"].iloc[-1]

Data caveats

Price fields and fundamentals fields are on different split-adjustment bases.

adj_close is split-only adjusted (no dividend adjustment). All fundamentals per-share fields (eps_diluted, dividends_per_share) are as-reported with no split adjustment. Do not combine them for ratios (P/E, P/B, etc.) unless no split occurred between the reporting date and today.

Recipes

— Revenue comparison across big tech

compare.py
import xfinlink as xfl

df = xfl.fundamentals(
    ["AAPL", "MSFT", "GOOGL"],
    period_type="annual",
    fields=["revenue", "net_income", "operating_cash_flow"],
    start="2024-01-01",
)
print(df[["ticker", "period_end", "revenue", "net_income"]])

— AAPL cumulative return

backtest.py
import xfinlink as xfl

df = xfl.prices("AAPL", start="2024-01-01", end="2024-12-31")
df["cumulative_return"] = (1 + df["return_daily"]).cumprod() - 1
print(f"AAPL 2024 total return: {df['cumulative_return'].iloc[-1]:.1%}")

— Entity resolution: GM bankruptcy split

resolve.py
import xfinlink as xfl

info = xfl.resolve("GM")
for entity in info["data"]["GM"]["entities"]:
    print(f"{entity['name']}: {entity['ticker_valid_from']} → {entity['ticker_valid_to']}")
# General Motors Corporation (pre-2009 bankruptcy): 1962-07-02 → 2009-06-01
# General Motors Company: None → None (current)

Vibe coding

Path 1 — Code Generation

For developers with Python + IDE

  1. Get your API key (free)
  2. Install: pip install xfinlink
  3. Copy the context block below and paste it into Claude, ChatGPT, or Cursor as system context
  4. Describe what you want — the LLM writes working xfinlink code
context.md
Loading llms.txt…

Path 2 — Direct Access via MCP

Zero code — ask questions, get answers

  1. Get your API key (free)
  2. Pick your AI platform and connect using the server URL below
  3. Ask anything — “What’s AAPL’s P/E ratio?” or “Compare top 5 banks by ROE”

Server URL

MCP server URL
https://api.xfinlink.com/mcp?api_key=YOUR_API_KEY

Replace YOUR_API_KEY with your key from the dashboard.

Available tools: get_prices, get_fundamentals, get_metrics, resolve_ticker, search, get_index, get_insiders

Claude (Anthropic)

Option A — Web (claude.ai)

  1. Go to claude.ai and sign in
  2. Click your profile icon → SettingsConnectors
  3. Click + then Add custom connector
  4. Name: xfinlink, URL: https://api.xfinlink.com/mcp?api_key=YOUR_API_KEY
  5. Click Create
  6. Start a new chat and ask: “What’s NVDA’s P/E ratio?”

Available on Free (1 connector), Pro, Max, Team, and Enterprise plans.

Option B — Claude Desktop

  1. Open Claude Desktop → SettingsDeveloperEdit Config
  2. Add to claude_desktop_config.json:
claude_desktop_config.json
{
  "mcpServers": {
    "xfinlink": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://api.xfinlink.com/mcp?api_key=YOUR_API_KEY"]
    }
  }
}
  1. Restart Claude Desktop

ChatGPT (OpenAI)

Requires Plus, Pro, Team, Enterprise, or Edu plan.

  1. Open chatgpt.comSettingsApps & Connectors
  2. Scroll to Advanced Settings → toggle Developer Mode on
  3. Go back to Connectors → click Create
  4. Name: xfinlink, MCP server URL: https://api.xfinlink.com/mcp?api_key=YOUR_API_KEY, Authentication: None
  5. Click Create
  6. Start a new chat → click +MoreDeveloper ModeAdd sources → enable xfinlink
  7. Ask: “Compare AAPL and MSFT revenue growth over the past 3 years”

Grok (xAI)

Option A — Web (grok.com)

Requires a paid Grok account.

  1. Go to grok.comMenuConnectors
  2. Click Add custom connector
  3. Name: xfinlink, URL: https://api.xfinlink.com/mcp?api_key=YOUR_API_KEY
  4. Enable the connector per conversation
  5. Ask: “Screen the S&P 500 for stocks with P/E below 20”

Option B — xAI API (Developer)

python
from openai import OpenAI

client = OpenAI(
    api_key="your-xai-api-key",
    base_url="https://api.x.ai/v1",
)

response = client.responses.create(
    model="grok-4.20-reasoning",
    input=[{"role": "user", "content": "What's AAPL's quarterly revenue trend?"}],
    tools=[{
        "type": "mcp",
        "server_url": "https://api.xfinlink.com/mcp?api_key=YOUR_API_KEY",
        "server_label": "xfinlink",
    }],
)

Perplexity

Requires Pro, Max, or Enterprise plan.

  1. Go to perplexity.aiAccount SettingsConnectors
  2. Click + Custom connector → select Remote
  3. Name: xfinlink, MCP Server URL: https://api.xfinlink.com/mcp?api_key=YOUR_API_KEY, Authentication: None, Transport: Streamable HTTP
  4. Check acknowledgement box → click Add
  5. Click the xfinlink card to enable it
  6. In a new search, toggle xfinlink under Sources
  7. Ask: “What’s NVDA’s gross margin trend over the past 2 years?”

Cursor

Add to .cursor/mcp.json:

.cursor/mcp.json
{
  "mcpServers": {
    "xfinlink": {
      "url": "https://api.xfinlink.com/mcp?api_key=YOUR_API_KEY"
    }
  }
}

Rate limits & usage policy

RATE LIMITS

API key limits: Free: 100/day, Pro: 10,000/day.

Sign up to get your API key. View pricing for higher limits.

CONTACT

Get in touch.

We read everything.