Valuein
Python SDK

Python SDK Guide

valuein-sdk loads Valuein's Parquet tables into a local DuckDB engine. You write SQL or call a helper and get a pandas DataFrame back. Every fact carries the time the SEC accepted it, so you can query the data as it was known on any past date.

pip install valuein-sdk·PyPI·Examples on GitHub·Get an API key →

No key needed to start. WithoutVALUEIN_API_KEYthe SDK reads the free Sample dataset: S&P 500 companies, last 5 years. Every snippet on this page runs on it.

Install

One package from PyPI. pip and uv install the same thing.

bash
pip install valuein-sdk            # Python 3.10 or newer # or with uvuv add valuein-sdk                 # inside a uv projectuv pip install valuein-sdk         # into any virtual environment # optional: client.to_polars() needs the polars extrapip install "valuein-sdk[polars]"

API key

A key is optional. Without one you get the free sample, and the same code runs on your plan once you add one.

bash
# Optional. With no key the SDK reads the free sample (S&P 500, last 5 years).export VALUEIN_API_KEY="your_token" # Or keep it in a .env file. The SDK loads the first .env it finds from the# current directory upward, once per process. VALUEIN_NO_DOTENV=1 turns that off.echo 'VALUEIN_API_KEY=your_token' >> .env # In Python, an explicit key wins over the environment:#   ValueinClient(api_key="your_token")#   ValueinClient(api_key="")    # always the free sample

First query

client.run_query() runs read-only DuckDB SQL over the tables and returns a pandas DataFrame.

python
from valuein_sdk import ValueinClient # Apple's annual revenue, net income and diluted EPS, one row per fiscal year.# A 10-K repeats earlier years as comparatives, so keep only the latest-filed# value for each period before pivoting.sql = """WITH latest AS (    SELECT f.period_end, f.standard_concept, f.numeric_value    FROM fact f    JOIN "references" r ON r.cik = f.entity_id    WHERE r.symbol = 'AAPL'      AND r.is_active      AND f.fiscal_period = 'FY'      AND f.standard_concept IN ('TotalRevenue', 'NetIncome', 'EPSDiluted')    QUALIFY ROW_NUMBER() OVER (        PARTITION BY f.period_end, f.standard_concept        ORDER BY f.accepted_at DESC    ) = 1)SELECT    period_end,    MAX(CASE WHEN standard_concept = 'TotalRevenue' THEN numeric_value END) AS revenue,    MAX(CASE WHEN standard_concept = 'NetIncome'    THEN numeric_value END) AS net_income,    MAX(CASE WHEN standard_concept = 'EPSDiluted'   THEN numeric_value END) AS eps_dilutedFROM latestGROUP BY period_endORDER BY period_end DESC""" with ValueinClient() as client:  # no key: the free sample    print(client.plan)           # 'sample', 'sp500', 'pro' or 'full'    df = client.run_query(sql)   # pandas DataFrame    print(df)

Without SQL

Common reads without writing SQL: resolve identifiers, read one company, run a named template, open the source filings.

python
from valuein_sdk import ValueinClient with ValueinClient() as client:    # What a ticker or CIK resolves to. A CIK also reaches delisted companies.    print(client.resolve(["AAPL", 320193]))     # One company's rows from a table, with the point-in-time filter applied.    facts = client.read_table(        "fact",        tickers="AAPL",        columns=["standard_concept", "period_end", "fiscal_period", "numeric_value"],        limit=10,    )    print(facts)     # Named SQL templates. Pass parameters as keyword arguments.    print(client.list_templates()[:5])    df = client.run_template("fundamentals_by_ticker", ticker="AAPL")    print(df.head())     # Links to the SEC filings behind the numbers.    links = client.filing_links("AAPL", form_types=["10-K"], limit=3)    print(links[["form_type", "filing_date", "document_url"]])     # Restatement Radar: figures a later filing changed (every plan).    changed = client.restatements(ticker="AAPL", limit=5)    print(changed[["standard_concept", "period_end", "first_value", "current_value", "disclosure_class"]])

Point in time

Pass as_of to see only what the SEC had accepted by that moment. No look-ahead in backtests.

python
from datetime import datetime, timezone from valuein_sdk import ValueinClient # With as_of set, fact, ratio, factor_scores, earnings_signals and the price# tables only show rows the SEC had accepted on or before that moment.as_of = datetime(2024, 1, 1, tzinfo=timezone.utc)  # must be timezone-aware sql = """SELECT f.period_end, f.numeric_value AS revenue, f.accepted_atFROM fact fJOIN "references" r ON r.cik = f.entity_idWHERE r.symbol = 'AAPL'  AND r.is_active  AND f.fiscal_period = 'FY'  AND f.standard_concept = 'TotalRevenue'QUALIFY ROW_NUMBER() OVER (PARTITION BY f.period_end ORDER BY f.accepted_at DESC) = 1ORDER BY f.period_end DESC""" with ValueinClient(as_of=as_of) as client:    print(client.run_query(sql))  # nothing accepted after 2024-01-01     # Index members on a past date, from index_membership.    members = client.pit_universe("2023-06-30")    print(members[["cik", "company_name", "ticker_at_date"]].head())

Screener

Screen the current S&P 500 on precomputed ratios, or start from the factor table.

python
from valuein_sdk import ValueinClient # Current S&P 500 members with return on equity above 20% and debt-to-equity# below 1.0 in their latest annual ratios. `ratio` holds FY and TTM rows,# so filter fiscal_period.sql = """WITH members AS (    SELECT DISTINCT cik FROM index_membership    WHERE index_name = 'SP500' AND removal_date IS NULL),latest AS (    SELECT entity_id, ratio_name, value    FROM ratio    WHERE fiscal_period = 'FY'      AND ratio_name IN ('return_on_equity', 'debt_to_equity')    QUALIFY ROW_NUMBER() OVER (        PARTITION BY entity_id, ratio_name        ORDER BY period_end DESC    ) = 1)SELECT    r.symbol,    r.name,    r.sector,    MAX(CASE WHEN l.ratio_name = 'return_on_equity' THEN l.value END) AS roe,    MAX(CASE WHEN l.ratio_name = 'debt_to_equity'   THEN l.value END) AS debt_to_equityFROM latest lJOIN members m ON m.cik = l.entity_idJOIN "references" r ON r.cik = l.entity_id AND r.is_active AND r.is_primary_tickerGROUP BY r.symbol, r.name, r.sectorHAVING roe > 0.20 AND debt_to_equity < 1.0ORDER BY roe DESCLIMIT 20""" with ValueinClient() as client:    print(client.run_query(sql))     # Or use the precomputed factor table (ranks are 0-1, 1 = best).    top = client.factor_scores(sector="Information Technology", min_composite_rank=0.8, limit=10)    print(top[["symbol", "period_end", "roe", "composite_rank"]])

Financial statements

client.financials() returns the three statements as the market knew them, and every cell traces to its filing.

python
from valuein_sdk import ValueinClient with ValueinClient() as client:  # no key: the free sample    # Three statements as the market knew them, one column per fiscal year.    fin = client.financials("AAPL", freq="FY", periods=4)    print(fin.summary())    print(fin.statement("income").head(8))     # Trace one cell to the SEC filing it came from.    trail = fin.explain("TotalRevenue", "2024-09-28")    print(trail[["concept", "value", "form_type", "accession_id", "sec_url"]])

DCF and peers

Run a DCF and pull peer comparables locally. The inputs are reported figures with lineage; you supply the growth rate.

python
from valuein_sdk import ValueinClient with ValueinClient() as client:  # no key: the free sample    # A five-year DCF on Apple's reported free cash flow. You choose the growth    # rate; the SDK never guesses one. Inputs trace to filings.    dcf = client.dcf("AAPL", stage1_growth_rate=0.06)    print(dcf.projection_frame())      # projected cash flows and present values    print(dcf.sensitivity_frame())     # value per share across WACC and terminal growth     # Peer comparables from reported ratios.    peers = client.peers("AAPL", n=3)    print(peers[["ticker", "cik"]])

Point-in-time screen

client.screen() builds a point-in-time screen. Requires Pro or Institutional (it reads daily prices).

python
from valuein_sdk import ValueinClient with ValueinClient() as client:  # no key: the free sample    # The S&P 500 as it stood on 2024-12-31, keeping only names priced above $10.    # Every field is read as it was known on that date: no look-ahead.    screen = client.screen("2024-12-31", universe="SP500", min_price=10)    print(screen.describe())     # Screens are lazy. collect() runs it and returns the result.    result = screen.collect()    print(result)

Factor backtest

client.factor_backtest() ranks a universe on a signal at each rebalance date with no look-ahead. Requires Pro or Institutional.

python
from valuein_sdk import ValueinClient # Needs Pro or Institutional: the backtest reads daily prices.with ValueinClient() as client:  # reads VALUEIN_API_KEY    # Rank the S&P 500 on return on equity each quarter, using only data the    # SEC had accepted at each rebalance date. Long the top quintile, short the bottom.    result = client.factor_backtest(        "return_on_equity",        start="2020-01-31",        end="2023-12-31",        freq="QE",        lineage=True,  # keep the fact_id behind every signal value    )    print(result.summary())    print(result.to_frame().head())

Restatement impact

client.restatement_impact() reruns a factor study on point-in-time, as-filed and restated data. Requires Pro or Institutional.

python
from valuein_sdk import ValueinClient # Needs Pro or Institutional: the study reads daily prices.with ValueinClient() as client:  # reads VALUEIN_API_KEY    # The same factor study on three vintages of the data: point-in-time,    # as originally filed, and as restated today. The gap is what restatements    # would have hidden from a backtest that used today's numbers.    study = client.restatement_impact(        "return_on_equity", start="2020-01-31", end="2023-12-31"    )    print(study.summary())

Bundles and sync

Write a reproducible offline bundle, mirror tables to disk, and replay them with no network.

python
import tempfilefrom pathlib import Path from valuein_sdk import ValueinClient out = Path(tempfile.mkdtemp()) / "apple-bundle" with ValueinClient() as client:  # no key: the free sample    # Write a point-in-time slice to disk: a few tables, one company.    # bundle.json records each file's SHA-256, the plan and the as_of cutoff.    client.write_bundle(out, tables=["entity", "references", "fact"], tickers=["AAPL"])    print(sorted(p.name for p in out.iterdir()))     # Mirror whole tables to disk. Re-running moves only what changed.    report = client.sync(out.parent / "mirror", tables=["entity", "index_membership"], verify=True)    print(report.downloaded) # Replay the bundle offline, with the same point-in-time views. Hashes are checked on load.with ValueinClient.from_bundle(out) as offline:    print(offline.is_offline)    print(offline.run_query("SELECT COUNT(*) AS facts FROM fact"))

Account and limits

Check your plan, rate budget and what the SDK can read.

python
from valuein_sdk import ValueinClient with ValueinClient() as client:  # no key: the free sample    print(client.plan)            # 'sample', 'sp500', 'pro' or 'full'    print(client.usage())         # rate budget used so far    print(client.capabilities())  # what this plan can read and do    print(client.diagnostics())   # versions, cache, connectivity

Errors

Every SDK error subclasses ValueinError. Catch the specific ones you can act on.

python
from valuein_sdk import (    ValueinClient,    ValueinError,    ValueinNotFoundError,    ValueinRateLimitError,    ValueinValidationError,) try:    with ValueinClient() as client:        df = client.read_table("fact", tickers="ZZZZZ", limit=5)except ValueinNotFoundError as e:    # An unknown ticker, or a CIK outside your plan's universe.    print(f"Not found: {e}")except ValueinValidationError as e:    # Bad input: a non-SELECT statement, a blocked function, a naive as_of.    print(f"Invalid request: {e}")except ValueinRateLimitError as e:    # Over your plan's per-minute or per-hour limit.    print(f"Rate limited, retry after {e.retry_after} s")except ValueinError as e:    # Base class of every SDK error (auth, plan, network, data).    print(f"Valuein error: {e}")

Command line

The package also installs a valuein command. Every command works without a key.

bash
valuein tables                     # tables your plan can readvaluein schema fact                # columns and types, no downloadvaluein query "SELECT symbol, name, sector FROM \"references\" WHERE is_active LIMIT 5"

Quick reference

The public methods. Every company argument takes a ticker or a CIK. Methods marked Pro need daily prices.

MethodReturnsWhat it does
ValueinClient(as_of=None, api_key=None, tables=None, config=None, bundle=None, snapshot=None)clientOpens a session. Use it as a context manager. No key means the free sample; as_of must be timezone-aware. ValueinConfig(progress=...) reports download progress as ProgressEvent.
client.run_query(sql)DataFrameRead-only DuckDB SQL (SELECT, WITH, EXPLAIN) over tables by name. Quote "references".
client.duckdb()read-only DuckDB sessionA DuckDB connection on the client's point-in-time views, for your own cursor, Arrow or relational API calls.
client.read_table(table, columns=, limit=, tickers=)DataFrameA whole table, or one company's rows by ticker or CIK, with the as_of filter applied.
client.run_template(name, **params)DataFrameRuns a named SQL template. Keyword arguments only; tickers in upper case.
client.list_templates()list[str]Names of every template run_template accepts.
client.resolve(identifiers)DataFrameWhat each ticker or CIK resolves to. A CIK also reaches delisted companies.
client.financials(ticker, freq="FY", periods=8, statements=, concepts=, vintage="pit")FinancialsIncome statement, balance sheet and cash flow as known at as_of. freq is FY, Q or TTM; vintage is pit or as_filed. fin.statement(), fin.ratios(), fin.summary(), fin.to_frame() and fin.explain(concept, period_end) trace any cell to its SEC filing.
client.dcf(ticker, stage1_growth_rate=, stage1_years=, terminal_growth_rate=, wacc=, fcf_base_override=, shares_override=)DcfResultA local discounted cash flow on reported free cash flow. You supply the growth rate. projection_frame(), sensitivity_frame() and inputs_frame() show the working.
client.peers(ticker, n=5, peers=, categories=)DataFramePeer comparables from reported ratios, with the target company first.
client.filing_links(ticker, form_types=, limit=)DataFrameSEC EDGAR links for a company's filings, newest first.
client.restatements(ticker=, disclosure_class=, severity=, min_delta_pct=, limit=)DataFrameRestatement Radar: figures a later filing changed. Every plan.
client.pit_universe(as_of_date, index="SP500")DataFrameIndex members on a past date (SP500, RUSSELL1000, RUSSELL2000, RUSSELL3000).
client.universe(index_name, dates=, start=, end=, freq=), client.rebalance_dates(start, end), client.trading_days(start, end)DataFrame, list, DatetimeIndexIndex members on each rebalance date, and the calendars behind them. Read daily prices: Pro and Institutional.
client.pit_panel(tickers, dates, concepts=), client.pit_valuation_ratios(ticker, as_of)DataFrameFacts, and multiples such as P/E and EV/EBITDA, as of each date. Price fields need Pro or Institutional.
client.screen(as_of=None, universe=, min_dollar_volume=, min_price=, max_age_days=)ScreenA lazy point-in-time screen. Chain .where(), .select(), .rank(), .top() and .preset(), then .collect(). Pro and Institutional.
client.signal_panel(universe, ratios=, concepts=, factors=, library=, lineage=False)DataFrameOne row per company and rebalance date with the signals you ask for. lineage=True keeps the fact_id behind each value. Pro and Institutional.
client.forward_returns(universe, execution_lag=1, horizons=(1,))DataFrameForward returns for each universe row, after an execution lag. Pro and Institutional.
client.factor_backtest(signal, start=, end=, index_name=, freq=, quantiles=, lineage=False, vintage=)BacktestResultQuantile backtest of one signal with no look-ahead. Results share summary(), to_frame(), to_dict(), plot() and explain(). Pro and Institutional.
factor_report(panel, signal=, quantiles=5), information_coefficient(...)FactorReportTear sheet of one signal over a panel from forward_returns(signal_panel(...)). Pure pandas. Import from valuein_sdk.
client.restatement_impact(signal, start=, end=, freq=, index_name=, quantiles=)VintageComparisonThe same factor study on point-in-time, as-filed and latest data: what restatements would have changed. Pro and Institutional.
client.study(name)context managerCounts the backtest trials you run, so multiple-testing haircuts use the real number. Pro and Institutional.
client.factor_scores(...), client.earnings_signals(...)DataFramePrecomputed factor ranks and earnings surprises.
client.stock_price(ticker, date=None)DataFrameLatest or as-of close: month-end on Sample and Benchmark, daily on Pro and Institutional.
client.prices(tickers), price_panel(tickers, start, end), total_return(ticker, start, end), return_series(...), corporate_actions(...), price_health()query, DataFrame, float, SeriesDaily bars, returns and price quality checks. Pro and Institutional only.
client.insider_transactions(ticker), institutional_holdings(...), advisers(...), adviser_private_funds(crd)DataFrameInsider trades, 13F holdings and Form ADV advisers. Institutional only.
client.to_arrow(sql), to_polars(sql), stream(sql), stream_arrow(sql)Arrow table, Polars, iteratorThe same SQL in other formats; stream yields DataFrames in batches. to_polars needs the polars extra.
client.sync(path, tables=, verify=False)SyncReportMirrors the plan's table files into a folder; re-running moves only changes. Open the mirror with ValueinClient.from_bundle(path).
client.write_bundle(path, tables=, tickers=, start=, end=), ValueinClient.from_bundle(path)BundleManifest, clientWrites a point-in-time slice with SHA-256 hashes, and replays it offline, reproducibly.
client.get_schema(table)dict[str, str]Column names and types.
client.tables()list[str]Every table your plan can read.
client.get(table)DataFrameThe published file as-is, without the as_of filter. Prefer read_table.
client.plan, client.me(), client.usage(), client.capabilities(), client.diagnostics(), client.manifest()str, dict, UsageReportYour plan, account, rate budget, what the SDK can do, a health report, and the snapshot date and table list.
client.report_issue(message, category=, severity=, ticker=, fact_id=, expected_value=)receiptSends a bug, data-quality report or feature request to Valuein, optionally pointing at one fact_id.

valuein/valuein on GitHub

Docs, examples, and support. Open an issue to ask a question or report a problem.

Need more than the sample?

Benchmark is free with an email: every S&P 500 constituent back to 1993. Pro and Institutional add the full US universe, delisted companies included. One key works for the SDK, the MCP server and the Bulk API.