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

Index names are trademarks of their owners; Valuein is not affiliated with or endorsed by any index provider.