valuein-sdk
Point-in-time, survivorship-free SEC EDGAR fundamentals for Python.
valuein-sdk gives you standardized financial statements from SEC filings back to
1993, queried with SQL in an in-process DuckDB. Every query sees only what was public
at your as_of date: rows are filtered on accepted_at, the moment the SEC accepted
the filing. Delisted, acquired and bankrupt companies stay in the data, and index
membership is stored as historical spells, so a universe on a past date is the one that
existed then. Every fact carries a fact_id and the accession number of the filing it
came from, so any number can be traced back to its source document.
Documentation · Examples · SQL cookbook · Schema · Pricing
Install
pip install valuein-sdk
Python 3.10 or newer. Optional extras:
| Extra | Adds |
|---|---|
valuein-sdk[polars] |
client.to_polars() and PriceQuery.to_polars() |
valuein-sdk[research] |
NumPy, Matplotlib and a Jupyter kernel for notebooks |
valuein-sdk[otel] |
OpenTelemetry spans (emitted only if your app configures a tracer) |
valuein-sdk[all] |
polars, otel, NumPy and Matplotlib |
Quickstart: 30 seconds, no token
With no API key the client runs on the free sample tier: S&P 500 constituents over the last five years. There is nothing to sign up for.
from valuein_sdk import ValueinClient
with ValueinClient() as client: # no token: free sample tier
print(client.resolve("AAPL")) # ticker -> CIK, name, active flag
revenue = client.run_template(
"fundamentals_by_ticker",
ticker="AAPL",
form_types=["10-K"],
metrics=["TotalRevenue", "NetIncome"],
)
print(revenue.head())
resolve returns one row per identifier with cik, symbol, name, is_active,
valid_from, valid_to and matched_by. The template returns one row per fact:
filing_date, report_date, period_end, fiscal_period, standard_concept,
numeric_value, unit. A 10-K also carries prior-year comparatives, so use
period_end, not report_date, to tell which year a number belongs to.
The same data from the shell:
valuein query 'SELECT symbol, name, sector FROM "references" WHERE is_active LIMIT 5'
valuein doctor # plan, snapshot, tables and cache, with no secrets
To use your plan, set a token and run the same code:
export VALUEIN_API_KEY="your_token" # or put it in a .env file
Three lines to a sourced income statement
from valuein_sdk import ValueinClient
client = ValueinClient() # no token: free sample tier, as_of = now (UTC)
client.financials("AAPL").income # fiscal years; .lineage traces every cell to its filing
financials() returns .income, .balance and .cash_flow (one row per standardized
concept, one column per period end; freq="Q" for standalone quarters, "TTM" for
trailing twelve months) and .lineage: one row per number with its fact_id,
accession, form type, acceptance time, and when (if ever) a later filing changed it.
.as_filed() shows each period as first reported, before any restatement;
.explain("TotalRevenue", "2024-09-28") lists the facts behind one cell with EDGAR links.
Five lines to a factor tear sheet (Pro or Institutional)
from valuein_sdk import ValueinClient
client = ValueinClient() # Pro or Institutional: the study reads daily prices
result = client.factor_backtest("gpoa", start="2015-01-01", end="2025-06-30", freq="QE")
report = result.tearsheet() # IC decay, quantile curves, turnover, exclusions
report # renders in a notebook; print(report.summary()) elsewhere
gpoa (gross profit over assets) is one of the library factors computed point in time
from the filings (valuein_sdk.quant.describe_library() lists every formula). The
backtest ranks the S&P 500 members of each quarter-end, trades one day later and pays
10 bps per unit traded; the tear sheet adds the information coefficient at one, two and
four quarters, the return of each quantile, turnover and every exclusion.
Core ideas
| Idea | What it means in code |
|---|---|
| Point in time | ValueinClient(as_of=...) takes a timezone-aware datetime (default: now, UTC). Point-in-time tables are filtered to accepted_at <= as_of. If that filter cannot be applied, the SDK raises ValueinIntegrityError instead of returning unfiltered rows. |
| Restatements | A later filing that reports a period again adds new rows with their own accepted_at. Under an as_of you see what was public then. The ratio view keeps only the latest vintage per ratio and period as of the cutoff. |
| Survivorship-free | references and entity include inactive companies. index_membership stores each membership as a spell (effective_date, removal_date), so pit_universe(date) returns the index as it was on that date. |
| CIK over ticker | Every method that takes a ticker also takes a CIK ("0000320193", "320193" or 320193). A CIK never changes; a ticker can be retired and later reused by another company. |
| Traceable numbers | Each fact row has a fact_id (SHA-256 of entity_id, accession_id, concept, period_end, unit) and the accession_id of its SEC filing. FactRecord.verify() recomputes the hash; filing_links() gives the EDGAR URLs. |
| Local compute | Queries run in an in-process DuckDB over Parquet streamed from the Valuein gateway. Downloads are cached on disk. There is no database to set up. |
Recipes
Every recipe runs on the free sample tier unless its heading says otherwise.
Get fundamentals for a ticker as of a date
from datetime import datetime, timezone
from valuein_sdk import ValueinClient
as_of = datetime(2024, 6, 30, tzinfo=timezone.utc)
with ValueinClient(as_of=as_of) as client:
facts = client.read_table("fact", tickers="AAPL")
assert (facts["accepted_at"] <= as_of).all() # nothing from after the cutoff
annual_revenue = (
facts[(facts["standard_concept"] == "TotalRevenue") & (facts["fiscal_period"] == "FY")]
.sort_values("accepted_at")
.drop_duplicates("period_end", keep="last") # latest value known on as_of
.sort_values("period_end")
)
print(annual_revenue[["period_end", "numeric_value", "unit", "accepted_at", "fact_id"]])
read_table(table, tickers=...) fetches only that company's partition where the
gateway serves one, and applies the same as_of filter. For backtests use
numeric_value. With an explicit as_of (or a bundle), value_current, restated
and restatement_count are recomputed from the filings accepted by the cutoff;
value_as_filed is as exported. Without an explicit as_of they are the latest
export's values.
Screen a universe without survivorship bias
from datetime import datetime, timezone
from valuein_sdk import ValueinClient
as_of = datetime(2023, 6, 30, tzinfo=timezone.utc)
with ValueinClient(as_of=as_of) as client:
members = client.pit_universe("2023-06-30") # S&P 500 on that date
roe = client.run_query(
"""
SELECT entity_id AS cik, value AS roe, period_end
FROM ratio
WHERE ratio_name = 'return_on_equity'
AND fiscal_period = 'FY'
AND period_end >= DATE '2022-06-30' -- skip stale fiscal years
QUALIFY row_number() OVER (PARTITION BY entity_id ORDER BY period_end DESC) = 1
"""
)
screen = members.merge(roe, on="cik").nlargest(10, "roe")
print(screen[["cik", "ticker_at_date", "company_name", "roe", "period_end", "removal_date"]])
print("members later removed from the index:", members["removal_date"].notna().sum())
pit_universe returns one row per member with cik, company_name, ticker_at_date,
sector, industry, effective_date and removal_date. Companies that left the index
after the date are still members on it. The ratio table holds annual (fiscal_period = 'FY') and trailing-twelve-month ('TTM') rows: always filter on fiscal_period, or
each company appears twice.
Backtest a factor (Pro or Institutional)
The quant layer needs daily prices (stock_price_daily), which only the Pro and
Institutional plans carry.
from valuein_sdk import ValueinClient
with ValueinClient() as client: # reads VALUEIN_API_KEY
result = client.factor_backtest(
"return_on_equity", # a ratio_name, a factor_scores column, or "mom_12m"
start="2018-01-01",
end="2024-12-31",
freq="QE",
)
print(result.summary()) # net and gross results, with the execution lag and cost used
print(result.mean_ic, result.ic_ir) # rank information coefficient and its stability
print(result.quantile_summary()) # mean return per quantile
factor_backtest builds the universe at each rebalance date from index membership,
reads each signal value as of that date, prices trades with a one-day execution lag
(execution_lag=1) and charges 10 bps per unit traded (cost_bps=10.0). Positions in
companies that delist are truncated, not dropped, and the summary lists what the study
could not use. For a custom signal, run the three steps yourself:
from valuein_sdk import ValueinClient, backtest
with ValueinClient() as client:
universe = client.universe("SP500", start="2018-01-01", end="2024-12-31", freq="QE")
panel = client.signal_panel(universe, ratios=["return_on_equity", "debt_to_equity"])
panel = client.forward_returns(panel) # appends forward-return columns
panel["score"] = panel["return_on_equity"].rank(pct=True) - panel["debt_to_equity"].rank(pct=True)
result = backtest(panel, signal="score", quantiles=5, cost_bps=10.0)
print(result.summary())
From the shell: valuein backtest return_on_equity --start 2018-01-01 --end 2024-12-31 --freq QE. More recipes, including Monte Carlo and reproducible bundles, are in the
Python SDK docs.
Prices and total return
Every tier has stock_price: one close per company at each fiscal period end and each
completed month end, with the listing's total_return_index (splits and dividends
reinvested).
from valuein_sdk import ValueinClient
with ValueinClient() as client:
print(client.stock_price("AAPL")) # latest row
monthly = client.run_query(
"""
SELECT price_date, close, total_return_index
FROM stock_price
WHERE entity_id = '0000320193' AND observation = 'monthly'
ORDER BY price_date
"""
)
tri = monthly["total_return_index"]
print(f"12-month total return: {tri.iloc[-1] / tri.iloc[-13] - 1:.1%}")
Daily bars (stock_price_daily) are on Pro and Institutional:
from valuein_sdk import ValueinClient
with ValueinClient() as client:
bars = (
client.prices(["AAPL", "MSFT"])
.between("2024-01-01", None)
.fields("close", "total_return")
.to_pandas()
)
growth = client.total_return("AAPL", "2020-01-01", "2024-12-31") # a fraction
# one column per ticker
panel = client.price_panel(["AAPL", "MSFT"], "2024-01-01", "2024-12-31")
Use a total-return basis (total_return_index, or the total_return field, which is
the default) for return math. Raw close is not split-adjusted, and adding div_cash on
top of a total-return series counts dividends twice. stock_price_daily has one row per
security per day, so a company with two share classes has two rows per day; the SDK's
price methods pick one bar per company per day for you.
Run a SQL template
The package ships parameterized SQL templates for common analyses: margins, free cash flow, Piotroski and Altman inputs, DuPont, restatement history, filing delays and more.
from valuein_sdk import ValueinClient
with ValueinClient() as client:
print(client.list_templates())
fcf = client.run_template(
"free_cash_flow",
ticker="MSFT",
form_types=["10-K"],
start_date="2021-01-01",
end_date="2025-12-31",
)
print(fcf.head())
Pass parameters as keywords: run_template(name, **params). Every value is validated
and quoted before the SQL is rendered: tickers such as "BRK-B" (share classes take a
hyphen, as references spells them), XBRL
frames such as "CY2023", "CY2024Q1" or "CY2024Q1I", fiscal_period one of Q1
to Q4, FY or TTM, and min_rank a percentile from 0.0 to 1.0. Common
parameters you omit get defaults: form_types is ["10-K", "10-Q"], start_date five
years ago, end_date today, and fiscal_period is "FY" for a template that reads
ratio (otherwise "Q1").
Templates read the same point-in-time views as run_query. The smart-money templates
(insider_buys, blockholders, top_institutional_holders, manager_portfolio) need
Institutional; price_history, price_return and price_momentum_screen need Pro or
Institutional. Copy-paste SQL patterns are in the
SQL cookbook.
Raw SQL over the tables
from valuein_sdk import ValueinClient
with ValueinClient() as client:
current_members = client.run_query(
"""
SELECT r.cik, r.symbol, r.name, r.sector
FROM "references" r
JOIN index_membership im ON im.cik = r.cik
WHERE im.index_name = 'SP500'
AND im.removal_date IS NULL
AND r.is_active AND r.is_primary_ticker
ORDER BY r.symbol
"""
)
print(current_members.head())
- Tables mount on first use; a query can reference any table on your plan.
"references"is an SQL keyword: keep the double quotes.run_queryaccepts oneSELECT,WITHorEXPLAINstatement. Writes, file and network functions (read_parquet,glob, ...) and system catalogs are rejected.- Other outputs:
client.to_arrow(sql),client.to_polars(sql), andclient.stream(sql)for results too large to hold in memory. - Columns:
client.get_schema("fact"), or the schema reference.
Verify a number back to the SEC filing
from valuein_sdk import FactRecord, ValueinClient
with ValueinClient() as client:
facts = client.read_table("fact", tickers="AAPL")
row = (
facts[(facts["standard_concept"] == "TotalRevenue") & (facts["fiscal_period"] == "FY")]
.sort_values(["period_end", "accepted_at"])
.iloc[-1]
)
record = FactRecord.model_validate(row.to_dict())
assert record.verify() # fact_id matches the hash of its identity fields
links = client.filing_links("AAPL", form_types=["10-K"])
filing = links[links["accession_id"] == record.accession_id].iloc[0]
print(record.numeric_value, record.unit, record.period_end, record.fact_id)
print(filing["inline_viewer_url"] or filing["document_url"] or filing["sec_url"])
filing_links returns accession_id, form_type, filing_date, accepted_at and
four EDGAR URLs per filing: inline_viewer_url (the Inline XBRL viewer; None for older
filings), document_url, viewer_url and sec_url (the filing index, always present).
compute_fact_id(entity_id, accession_id, concept, period_end, unit) computes the same
hash as a plain function.
Track restatements
from valuein_sdk import ValueinClient
with ValueinClient() as client:
events = client.restatements(ticker="AAPL")
columns = ["period_end", "standard_concept", "first_value", "current_value", "disclosure_class"]
print(events[columns])
restatements() reads restatement_events on every tier, including with no token.
Each row is one reported value that a later filing changed, with both accession
numbers. disclosure_class is non_reliance (an 8-K Item 4.02 notice), amended (a
10-K/A or 10-Q/A) or undisclosed (the change appeared in a routine filing). Filter with
disclosure_class=, severity= and min_delta_pct=.
Value a company and compare its peers
from valuein_sdk import ValueinClient
with ValueinClient() as client:
result = client.dcf("AAPL", stage1_growth_rate=0.08)
table = client.peers("AAPL", n=5)
print(result.value_per_share, result.price, result.upside)
print(result.inputs_frame()[["name", "value", "fact_id", "accession_id"]])
print(table[["ticker", "is_subject", "pe_ratio", "roe"]])
dcf() is the MCP compute_dcf model (two-stage, free cash flow growing at your
stage1_growth_rate, discounted at wacc, default 9%) computed in-process from the
annual filings known at the client's as_of: nothing is sent to the MCP server. Every input
(operating cash flow, capex, debt, cash, shares) carries the fact_id and filing it came
from, defaults are flagged in result.assumptions, and projection_frame() and
sensitivity_frame() give the year-by-year cash flows and a 5x5 grid. The growth rate is
yours: the SDK never guesses one. peers() returns the subject first, then companies sharing
its SIC code (S&P 500 members first), with the pipeline ratios as of the same date; pass
peers=["MSFT", "GOOGL"] to choose them yourself. Formatted DCF and comps workbooks
come from the Valuein Workspace or the MCP server's document tools.
Mirror a snapshot locally and pin it
client.sync(path) copies your plan's published table files into a directory, byte for
byte, with a SHA-256 and the published ETag for each. Running it again moves only the
tables that were republished: an unchanged table costs one body-free request. The mirror
is also a research bundle, so it replays offline (an air-gapped host included) through the
same point-in-time views, and a client pinned to its snapshot refuses to read any other.
from valuein_sdk import ValueinClient
with ValueinClient() as client:
report = client.sync("./valuein-mirror", tables=["references", "fact"])
print(report.snapshot, report.downloaded, report.bytes_downloaded)
with ValueinClient.from_bundle("./valuein-mirror") as offline:
revenue = offline.run_query(
"SELECT entity_id, period_end, numeric_value FROM fact "
"WHERE standard_concept = 'TotalRevenue' LIMIT 5"
)
with ValueinClient(snapshot=report.snapshot) as pinned:
assert pinned.manifest()["snapshot"] == report.snapshot
The gateway serves only its current snapshot. Once it publishes the next one, the pinned
client raises ValueinSnapshotError; the mirror still reads that snapshot offline. A sync
writes nothing if the snapshot changes while it runs, so a mirror never mixes two.
Plans and tiers
The SDK reads your plan from your token; your code does not change between plans. The same token unlocks the bulk data API, the MCP server and the Valuein Workspace at your plan.
| Plan | Key in code | How to get it | Coverage |
|---|---|---|---|
| Sample | sample |
No token | S&P 500 constituents, last five years |
| Benchmark | sp500 |
Free registration | Every S&P 500 constituent, full history |
| Pro | pro |
Paid | Full US universe, plus daily prices (stock_price_daily) |
| Institutional | full |
Paid | Pro, plus insider, 13F/13D/13G and Form ADV tables |
client.plan shows the active plan. get_plans() returns each plan's request rate
limits from the gateway. Prices and history windows: valuein.biz/pricing.
Tables
The table list and every column come from the gateway's manifest at runtime: use
client.tables() and client.get_schema(table), and client.manifest() for the
snapshot and schema version you are reading. "PIT" means the view is filtered to
accepted_at <= as_of.
| Table | What it holds | PIT | Plans |
|---|---|---|---|
references |
Start here. One row per security: cik, symbol, name, sector, industry, is_active, is_primary_ticker, FIGI identifiers |
All | |
entity |
Company master: addresses, SIC code, state of incorporation, former names | All | |
security |
Ticker history with valid_from / valid_to |
All | |
filing |
One row per SEC filing: accession_id, form_type, filing_date, report_date, accepted_at, primary_document |
Yes | All |
filing_registrant |
Every registrant of a filing (accession_id, entity_id). filing lists a co-registrant filing under one CIK; join this table to filing on accession_id to list a company's filings including the combined ones. The join to filing applies the point-in-time filter |
Where the plan's manifest lists it | |
fact |
Standardized financial facts: standard_concept, raw XBRL concept, numeric_value, unit, period_end, fiscal_period, accepted_at, fact_id, accession_id. priority = 1 marks the tag elected for its concept in a filing, decided at export by tag precedence |
Yes | All |
ratio |
Computed ratios by company and period: ratio_name, category, value, fiscal_period (FY or TTM). confidence_score is computed over the full export history and is not point-in-time under a past as_of |
Yes | All |
standard_concept |
Dictionary of the standardized concepts in fact.standard_concept |
All | |
taxonomy_guide |
Reference for the raw us-gaap tags in fact.concept |
All | |
index_membership |
Index membership spells keyed on cik: index_name, effective_date, removal_date (exclusive) |
All | |
factor_scores |
Cross-sectional factor scores and percentile ranks by company and period | Yes | All |
earnings_signals |
EPS trend estimate and surprise, revenue growth, price momentum | Yes | All |
stock_price |
Close and total_return_index at period ends and completed month ends |
Yes | All |
stock_price_daily |
Daily OHLCV bars, div_cash, split_factor, total_return_index, is_primary_listing |
Yes | Pro, Institutional |
restatement_events |
Values a later filing changed, with both accessions and a disclosure_class |
All | |
| Smart money | insider_party, insider_filing, insider_transaction, insider_ownership, insider_footnote, insider_ownership_purpose, institutional_filing, institutional_holding, institutional_other_manager |
Yes, except insider_party |
Institutional |
| Form ADV | investment_adviser, investment_adviser_private_fund (keyed on CRD, not CIK) |
Institutional |
Join keys: fact.entity_id, filing.entity_id and security.entity_id hold the CIK;
fact.accession_id = filing.accession_id; index_membership.cik = "references".cik.
There is no is_sp500 column: join index_membership instead. For one quarter of a
cash-flow item use COALESCE(derived_quarterly_value, numeric_value), because Q2 and Q3
10-Qs report year-to-date figures.
API at a glance
Everything below imports from valuein_sdk. Every public method is typed and
documented: help(ValueinClient.read_table) shows the arguments, the returned columns
and the errors, and client.capabilities() returns the whole surface as a dictionary.
ValueinClient(as_of=None, api_key=None, gateway_url=None, tables=None, config=None, snapshot=None)
| Method | Returns | Use it to |
|---|---|---|
| Read data | ||
read_table(table, *, columns=None, limit=None, tickers=None, dtype_backend="numpy") |
DataFrame |
Read a table through its point-in-time view, optionally for some companies |
run_query(sql, *, dtype_backend="numpy") |
DataFrame |
Run one read-only DuckDB query; dtype_backend="pyarrow" returns Arrow-backed columns, faster and lighter on large results |
run_template(name, **params) |
DataFrame |
Run a packaged SQL template |
list_templates() |
list[str] |
List template names |
to_arrow(sql) / to_polars(sql) / stream(sql, batch_size=10_000) / stream_arrow(sql, batch_size=100_000) |
Arrow / Polars / iterator / RecordBatchReader |
Other output formats |
get(table) |
DataFrame |
Download the raw published table, without the as_of filter |
| Companies and universes | ||
resolve(identifiers) |
DataFrame |
See which company each ticker or CIK matches |
pit_universe(as_of_date, index="SP500") |
DataFrame |
Index members on a past date |
factor_scores(ticker=None, sector=None, min_composite_rank=None, limit=100) |
DataFrame |
Factor scores and ranks |
earnings_signals(ticker=None, min_surprise_pct=None, direction=None, limit=100) |
DataFrame |
EPS surprise and trend |
restatements(*, ticker=None, disclosure_class=None, severity=None, min_delta_pct=None, limit=100) |
DataFrame |
Restatement events |
filing_links(ticker, form_types=None, start_date=None, end_date=None, limit=20) |
DataFrame |
EDGAR URLs per filing |
financials(ticker, *, freq="FY", periods=8, statements=..., concepts=None, vintage="pit") |
Financials |
Income statement, balance sheet and cash flow as known at as_of (FY, standalone Q, TTM), each cell traced to its fact_id and filing; .as_filed(), .periods(), .ratios(), .explain() |
screen(as_of=None, *, universe="SP500", min_dollar_volume=None, min_price=None, max_age_days=None) |
Screen |
Point-in-time screen of the members on that date: .where("roic > 0.15"), .rank(...), .top(n), .preset(...), then .to_pandas() |
| Prices | ||
stock_price(ticker, date=None) |
DataFrame |
Latest (or dated) close; every tier |
prices(tickers) |
PriceQuery |
Daily bars: .between(), .fields(), .last(), .to_pandas(); Pro+ |
total_return(ticker, start, end, *, basis="total_return") |
float |
Total return over a window; Pro+ |
price_panel(tickers, start, end, *, field="total_return") |
DataFrame |
Wide price matrix; Pro+ |
pit_panel(tickers, dates, *, concepts=None) |
DataFrame |
Price and fundamentals known by the end (UTC) of each date; flows as trailing twelve months; Pro+ |
pit_valuation_ratios(ticker, as_of) |
DataFrame |
P/E, P/S, P/B and EV/Revenue on a date, on trailing-twelve-month flows; Pro+ |
dcf(ticker, *, stage1_growth_rate, stage1_years=None, terminal_growth_rate=None, wacc=None, fcf_base_override=None, shares_override=None) |
DcfResult |
Local forward DCF (the MCP compute_dcf model): EV, equity, per share, upside, projection and sensitivity frames, every input with its fact_id and filing |
peers(ticker, *, n=5, peers=None, categories=None) |
DataFrame |
Subject row first, then SIC-matched peers (or yours) with pipeline ratios (P/E, P/B, EV/EBITDA, margins, ROE, ROIC, FCF yield, ...) as of the client's as_of |
price_health() / corporate_actions(ticker) |
dict / DataFrame |
Price data coverage; splits and dividends; Pro+ |
| Quant research (Pro+) | ||
factor_backtest(signal, *, start, end, freq="ME", ...) |
BacktestResult |
Universe, signal, forward returns and backtest in one call; .tearsheet(), .explain(date) (with lineage=True), .robustness() |
restatement_impact(signal, *, start, end, freq="QE", ...) |
VintageComparison |
The same study on point-in-time, as-filed and latest vintages, with the restatement events behind the differences |
study(name) |
Study |
with client.study(...): counts every backtest as a trial; summaries then print the deflated Sharpe ratio, haircut Sharpe and PBO |
universe(index_name="SP500", *, dates=None, start=None, end=None, freq="ME") |
DataFrame |
Index members at each rebalance date (dates= works on every tier) |
signal_panel(universe, *, ratios=, concepts=, factors=, momentum_months=, library=, max_age_days=, vintage="pit", lineage=False) |
DataFrame |
Signal values accepted by the end (UTC) of each rebalance date; flow concepts (revenue, income, cash flow) as trailing twelve months; library= adds documented factors (ep, gpoa, mom_12_1, …, describe_library()); lineage=True traces each cell to its filings |
forward_returns(panel, *, execution_lag=1, horizons=(1,)) |
DataFrame |
Return to the next rebalance (and horizons intervals out), delisting-aware |
duckdb() |
ReadOnlySession |
A read-only DuckDB session over the same point-in-time views |
trading_days(start, end) / rebalance_dates(start, end, freq=) |
DatetimeIndex / list[str] |
Calendars built from actual trading days |
write_bundle(path, *, tables, ...) / ValueinClient.from_bundle(path) |
BundleManifest / client |
Freeze a data slice with SHA-256 hashes; replay it offline |
sync(path, *, tables=None, verify=False) |
SyncReport |
Mirror the published table files; a re-run moves only republished ones |
| Session | ||
plan / as_of |
str / datetime |
Active plan and cutoff |
tables() / get_schema(table) |
list[str] / dict |
Tables on your plan; column types |
me() / manifest() / health() |
dict |
Token, snapshot and gateway status |
capabilities() / diagnostics() / usage() |
dict / dict / report |
Machine-readable surface; redacted support bundle; download budget |
cache_info() / clear_cache(*, table=None) |
dict |
On-disk cache |
report_issue(message, *, category="bug", ...) |
FeedbackReceipt |
Report a bug or a data problem; works with no token |
close() |
Release DuckDB and HTTP resources (or use with) |
Also exported: backtest, factor_report, describe_library, to_alphalens,
block_bootstrap, bootstrap, gbm and correlated_gbm (every simulation requires a
seed), AlphaEngine with built-in factors such as ROE and GROSS_MARGIN,
FactRecord, compute_fact_id, ValueinConfig and the ValueinError hierarchy;
valuein_sdk.quant adds fama_macbeth, factor_returns, deflated_sharpe,
probability_of_backtest_overfitting, haircut_sharpe and explain_cell. Every result
object (BacktestResult, FactorReport, Financials, ScreenResult,
VintageComparison, Study, FamaMacBethResult, SimulationResult) has summary(),
to_frame(), to_dict() (strict JSON with params and provenance), plot() (needs
valuein-sdk[research]), explain() and a notebook view.
valuein_sdk.aio.AsyncValueinClient is the asyncio client.
Command line
The valuein command installs with the package and uses the same token rules.
| Command | Does |
|---|---|
valuein doctor |
Print a redacted diagnostics bundle |
valuein tables |
List tables on your plan |
valuein schema TABLE |
Show a table's columns and types |
valuein query "SQL" [--format csv|json] |
Run a read-only query |
valuein prices TICKER [--start] [--end] [--last N] |
Daily bars (Pro+) |
valuein health [TICKERS...] |
Price data coverage (Pro+) |
valuein backtest SIGNAL --start --end [--freq] [--quantiles] [--cost-bps] ... |
Factor backtest (Pro+) |
valuein bundle PATH --tables T1 T2 ... |
Write an offline research bundle |
valuein cache stats / valuein cache clear [--table T] |
Inspect or clear the cache, offline |
Configuration
Environment variables
| Variable | Effect |
|---|---|
VALUEIN_API_KEY |
Your token, read when api_key is not passed. A .env file found from the current directory upward is loaded once per process. |
VALUEIN_NO_DOTENV |
1, true, yes or on (any case) skips loading .env; any other value, 0 included, loads it. |
VALUEIN_GATEWAY_URL |
Override the data gateway URL (development). |
VALUEIN_MCP_URL |
Override the MCP server URL used by report_issue(). |
VALUEIN_PLANS_ENDPOINT |
Override the URL that get_plans() reads. |
VALUEIN_DISABLE_TELEMETRY |
1 or true turns off OpenTelemetry spans. |
VALUEIN_DEBUG |
Make the CLI print full tracebacks. |
XDG_CACHE_HOME |
Cache root (default ~/.cache); the SDK uses valuein-sdk/ under it. |
api_key=None (the default) reads the environment, api_key="" forces the sample
tier, and any other string is used as the token.
ValueinConfig
from valuein_sdk import ValueinClient, ValueinConfig
config = ValueinConfig(memory_limit="8GB", threads=8, safe_mode=True)
with ValueinClient(config=config) as client:
print(client.run_query('SELECT count(*) AS n FROM "references"'))
| Field | Default | Meaning |
|---|---|---|
memory_limit |
"4GB" |
DuckDB memory ceiling; DuckDB spills to disk above it |
threads |
4 |
DuckDB threads |
temp_directory |
None |
Spill and download directory; None uses a temporary directory removed on close() |
max_temp_directory_size |
"20GB" |
Cap on spill-disk use |
connect_timeout / read_timeout |
30.0 / 120.0 |
HTTP timeouts in seconds |
cache_enabled / cache_dir / cache_max_bytes |
True / None / 4 GiB |
On-disk Parquet cache |
cache_revalidate |
True |
Check a cached file's ETag once per session; if the check cannot run, the cached copy is used |
safe_mode |
False |
Block DuckDB file and network access; use it when running SQL written by a model |
hot_data |
True |
Include filings published since the last weekly snapshot in filing, fact and ratio |
hot_index_ttl_s / hot_max_partitions |
60.0 / 200 |
How often to re-check for those filings; most companies folded in per table |
download_streams |
4 |
Parallel ranged requests for a table file of 32 MiB or more, all pinned to one ETag; 1 is a single stream |
partition_fanout |
24 |
Most per-company files a read_table(..., tickers=...) fetches before it reads the whole table once instead |
progress |
None |
Download and mount progress: a callable receiving a ProgressEvent, or "auto" for a rich/tqdm bar in a terminal |
ValueinConfig is frozen: build a new one instead of changing a field.
Errors
Every SDK error subclasses ValueinError, and every message says what was wrong and how
to fix it.
| Exception | Raised when |
|---|---|
ValueinAuthError |
The token is invalid, expired or revoked |
ValueinPlanError |
The request needs a higher plan |
ValueinNotFoundError |
A table, ticker or CIK is not found, or is outside your plan's universe |
ValueinRateLimitError |
Too many requests; .retry_after holds seconds when the gateway sends it |
ValueinAPIError |
The gateway returned a 5xx; .status_code holds it |
ValueinConnectionError |
The gateway cannot be reached (also a ConnectionError) |
ValueinTimeoutError |
A request timed out after retries (also a TimeoutError) |
ValueinValidationError |
An argument, query or parameter is invalid (also a ValueError) |
ValueinDataError |
A query failed in DuckDB, a table could not be loaded for your plan, or a bundle file failed its hash check |
ValueinSnapshotError |
A client pinned with snapshot= finds another snapshot, or the snapshot changes during sync() (a ValueinDataError) |
ValueinIntegrityError |
The point-in-time filter could not be applied to a table |
ValueinConfigError |
ValueinConfig or a tables= list is invalid |
ValueinDependencyError |
An optional package is missing, e.g. matplotlib for plot(); the message names the extra to install (also an ImportError) |
FeedbackError |
The server rejected a report_issue() call |
from valuein_sdk import ValueinClient, ValueinError
with ValueinClient() as client:
try:
client.run_query("DELETE FROM fact")
except ValueinError as exc:
print(type(exc).__name__, exc) # ValueinValidationError and the reason
Using valuein-sdk with AI coding assistants
Paste this block into ChatGPT, Claude, Copilot or your agent's system prompt before asking for code that uses this library.
You are writing Python that uses the valuein-sdk library (PyPI: valuein-sdk).
Core API
- from valuein_sdk import ValueinClient
- Use it as a context manager:
with ValueinClient(as_of=datetime(2024, 6, 30, tzinfo=timezone.utc)) as client: ...
- as_of must be a timezone-aware datetime. It defaults to now (UTC). Point-in-time tables
(filing, fact, ratio, factor_scores, earnings_signals, stock_price, stock_price_daily
and the smart-money tables) only show rows with accepted_at <= as_of.
- client.read_table(table, columns=None, limit=None, tickers=None) -> DataFrame
- client.run_query(sql) -> DataFrame. One SELECT, WITH or EXPLAIN statement. Values go
inline as SQL literals. No read_parquet, no file or network functions.
- client.run_template(name, **params) -> DataFrame. Keyword arguments only.
client.list_templates() lists the names.
- Also: client.resolve(identifiers), client.pit_universe("YYYY-MM-DD"),
client.restatements(ticker=...), client.filing_links(ticker),
client.stock_price(ticker), client.get_schema(table), client.tables(),
client.capabilities().
- Quant layer, which needs a Pro or Institutional plan because it reads
stock_price_daily: client.factor_backtest(signal, start=, end=, freq=),
client.universe(), client.signal_panel(), client.forward_returns(),
valuein_sdk.backtest().
Rules
1. Identify companies by CIK (10 digits, zero-padded, e.g. "0000320193"). Resolve a
ticker once with client.resolve() and carry the CIK. Tickers get reused by other
companies; CIKs do not.
2. Never invent, estimate or round a financial number. Only report values the library
returned. If a query returns no rows, say so.
3. When you report a number from the fact table, cite its fact_id and accession_id. Use
client.filing_links() for the SEC URL.
4. For backtests, set as_of and use fact.numeric_value. With an explicit as_of,
value_current, restated and restatement_count are recomputed at the cutoff;
without one they are the latest export's values.
5. In SQL, write "references" with double quotes (it is a keyword). Index membership is
a JOIN on index_membership.cik; there is no is_sp500 column.
6. ratio has FY and TTM rows: always filter fiscal_period. A 10-K carries prior-year
comparatives: group by period_end, not report_date. For one quarter of a cash-flow
item use COALESCE(derived_quarterly_value, numeric_value).
7. For return math use total_return_index (or the SDK's total_return basis), never raw
close, and never add div_cash on top of it.
8. client.get(table) skips the as_of filter. Use read_table or run_query instead.
9. To run SQL you generated, create the client with
config=ValueinConfig(safe_mode=True).
10. SDK errors are ValueinError subclasses whose message states the fix. Read it before
retrying.
The SDK is data plus local compute: every query, ratio and backtest runs in your
process. Documents (DCF and comps workbooks, research briefs) come from the Valuein
Workspace or the MCP server at https://mcp.valuein.biz/mcp, which agents that call
tools instead of writing Python can use with the same token.
Examples and notebooks
Runnable examples live in the public valuein/valuein repository:
Support
- Documentation: valuein.biz/developers/python-sdk
- Questions and bugs: github.com/valuein/valuein/issues or support@valuein.biz
- A wrong number:
client.report_issue("...", category="data_quality", fact_id=..., ticker=...)sends it to the Valuein team from your code, with no token needed.
License
Apache License 2.0. See the license text.
This library and its data are for research and education. Nothing here is financial advice.
Metadata
Release files for valuein-sdk 7.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| valuein_sdk-7.2.0.tar.gz | 462.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| valuein_sdk-7.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.0 MB
Release files / valuein_sdk-7.2.0.tar.gz
| Download URL | valuein_sdk-7.2.0.tar.gz |
|---|---|
| Size | 462.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ca60c76f04a6f97871f22612a5144d39ec978087fbfaa841cb8232ee7471f3e3
|
|
BLAKE2b-256 checksum How to use checksums |
bb845bebd489dd8f76225617f235128063ad05bc9d36800cf86473c6412b5c55
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.
Transparency logRelease files / valuein_sdk-7.2.0-py3-none-any.whl
| Download URL | valuein_sdk-7.2.0-py3-none-any.whl |
|---|---|
| Size | 550.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9f6f6b0d101b71fe8645a4d31edddfe2ff5ceb8957fe197441c1962df3825a7f
|
|
BLAKE2b-256 checksum How to use checksums |
5d9dbbd57f5609c968ae516f7e0949c003bee32e849ba2e4d1bd94662992b498
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.
Transparency log