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.
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.
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.
# 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 sampleFirst query
client.run_query() runs read-only DuckDB SQL over the tables and returns a pandas DataFrame.
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.
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.
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.
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.
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.
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).
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.
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.
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.
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.
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, connectivityErrors
Every SDK error subclasses ValueinError. Catch the specific ones you can act on.
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.
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.
| Method | Returns | What it does |
|---|---|---|
ValueinClient(as_of=None, api_key=None, tables=None, config=None, bundle=None, snapshot=None) | client | Opens 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) | DataFrame | Read-only DuckDB SQL (SELECT, WITH, EXPLAIN) over tables by name. Quote "references". |
client.duckdb() | read-only DuckDB session | A 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=) | DataFrame | A whole table, or one company's rows by ticker or CIK, with the as_of filter applied. |
client.run_template(name, **params) | DataFrame | Runs 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) | DataFrame | What each ticker or CIK resolves to. A CIK also reaches delisted companies. |
client.financials(ticker, freq="FY", periods=8, statements=, concepts=, vintage="pit") | Financials | Income 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=) | DcfResult | A 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=) | DataFrame | Peer comparables from reported ratios, with the target company first. |
client.filing_links(ticker, form_types=, limit=) | DataFrame | SEC EDGAR links for a company's filings, newest first. |
client.restatements(ticker=, disclosure_class=, severity=, min_delta_pct=, limit=) | DataFrame | Restatement Radar: figures a later filing changed. Every plan. |
client.pit_universe(as_of_date, index="SP500") | DataFrame | Index 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, DatetimeIndex | Index 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) | DataFrame | Facts, 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=) | Screen | A 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) | DataFrame | One 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,)) | DataFrame | Forward 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=) | BacktestResult | Quantile 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(...) | FactorReport | Tear 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=) | VintageComparison | The same factor study on point-in-time, as-filed and latest data: what restatements would have changed. Pro and Institutional. |
client.study(name) | context manager | Counts the backtest trials you run, so multiple-testing haircuts use the real number. Pro and Institutional. |
client.factor_scores(...), client.earnings_signals(...) | DataFrame | Precomputed factor ranks and earnings surprises. |
client.stock_price(ticker, date=None) | DataFrame | Latest 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, Series | Daily bars, returns and price quality checks. Pro and Institutional only. |
client.insider_transactions(ticker), institutional_holdings(...), advisers(...), adviser_private_funds(crd) | DataFrame | Insider trades, 13F holdings and Form ADV advisers. Institutional only. |
client.to_arrow(sql), to_polars(sql), stream(sql), stream_arrow(sql) | Arrow table, Polars, iterator | The same SQL in other formats; stream yields DataFrames in batches. to_polars needs the polars extra. |
client.sync(path, tables=, verify=False) | SyncReport | Mirrors 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, client | Writes 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) | DataFrame | The 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, UsageReport | Your 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=) | receipt | Sends 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.