← tradevodata.com

API Documentation

One endpoint, one promise: ask what was knowable about a company on a date, and that's exactly — and only — what you get back.

Machine-readable: OpenAPI 3.1 spec — /openapi.json · live check: /status

Authentication

Send your API key with every request, either way:

x-api-key: tvd_your_key_here
# or
Authorization: Bearer tvd_your_key_here

Keys are issued on subscription and deactivated on cancellation. One key per subscription — don't share it. Two daily limits apply (5,000 requests, 2,500 distinct tickers) — see Limits.

GET/v1/fundamentals

Returns, for each concept, the most recent fiscal period whose 10-K had been filed on or before as_of — the point-in-time view. Note the boundary is inclusive: a 10-K filed on as_of itself counts as knowable that day (filings often land after market close — use the prior day if you need strict before-the-open semantics).

Query parameters

tickerrequired
US ticker symbol, e.g. AAPL. 1–12 chars (A–Z, 0–9, dot, dash). Unknown tickers return 404.
as_ofrequired
The knowledge date, YYYY-MM-DD. Must be a real calendar date.
concept
Filter to a single concept (case-insensitive). One of: Revenue, NetIncome, Assets, StockholdersEquity, OperatingCashFlow, EPSDiluted, DilutedShares. Omit for all seven.

Response fields

concept
Which fundamental this row is.
fiscal_year
Derived from period_end: the calendar year the fiscal period ends in, except 52/53-week closes falling Jan 1–7, which belong to the prior year. We deliberately do notuse the filing's own fiscal-year label — in XBRL that label is the year of the filing, so a prior-year comparative carries the newer year and collides with the real row. When a company changes its fiscal year end, one calendar year can legitimately contain two annual closes; period_end is the join key of record, not fiscal_year.
period_end
The date the fiscal period ended.
filed
When this value first became public — the point-in-time stamp.
value
The first-reported value — what was actually knowable at filed. Use this for backtests.
latest_value
The most recent revision of the value. May post-date your as_of — not point-in-time safe. Provided for reference only.
lag_days
Days from period_end to filed — the size of the lookahead gap this row closes.
data_through
Top-level. The newest filing date in the dataset. We serve a snapshot, so anything filed after this date is not reflected; if your as_of is later, the response also carries a coverage_warning.
restated
true if a later filing revised the value by more than 0.5% — a materiality threshold. latest_value may differ slightly from value without tripping the flag (sub-0.5% revisions are shown but not flagged). Revisions are tracked within the same XBRL tag across filings.
qa_status
"clean", or "FLAG:" plus semicolon-separated reasons: lag_out_of_range (filing lag outside 0–120 days), revenue_magnitude, ambiguous_tag (another XBRL element in the same filing reported materially more for this period, so which one is the true total is uncertain), implausible_value (a scale/unit-corrupted figure was suppressed — the value is null), tag_switch_discontinuity (the XBRL element behind this concept differs from the adjacent fiscal year — Broadcom alternates ProfitLoss and NetIncomeLoss, for instance. The value is usually correct; the flag marks a scope change to check before you difference year over year, not an error), and stale_for_as_of (the newest period we hold for this concept ends more than ~18 months before your as_of). We flag; we never quietly fix.

Example

curl "https://tradevodata.com/v1/fundamentals?ticker=AAPL&as_of=2024-06-30&concept=Revenue" \
  -H "x-api-key: tvd_your_key_here"
{
  "ticker": "AAPL",
  "as_of": "2024-06-30",
  "data_through": "2026-07-24",
  "count": 1,
  "fundamentals": [{
    "concept": "Revenue",
    "fiscal_year": 2023,
    "period_end": "2023-09-30",
    "filed": "2023-11-03",
    "lag_days": 34,
    "value": 383285000000,
    "latest_value": 383285000000,
    "restated": false,
    "qa_status": "clean"
  }]
}

Note the answer is FY2023, not FY2024 — on 2024-06-30 the FY2024 10-K had not been filed yet. That is the whole product.

Python

The official client. as_of is a requiredargument on every query — there is no way to ask it for “Apple's revenue”, only for what was knowable on a given date, which is how lookahead bias stops being something you can write by accident. Zero dependencies; pandas is optional.

pip install tradevodata
import tradevodata as tv

df = tv.sample()                    # free 40-company sample — no API key needed

client = tv.Client(api_key="tvd_...")           # or set TRADEVODATA_API_KEY
client.fundamentals("AAPL", as_of="2024-06-30") # -> FY2023
client.snapshot(as_of="2024-06-30", concept="Revenue", to_pandas=True)

Source: github.com/christianpichichero-max/tradevodata-py · PyPI

Note: as of June 2024, the newest knowable Apple annual revenue is FY2023 — FY2024 wasn't filed until 2024-11-01. That's the product.

Python (plain requests)

import requests

r = requests.get(
    "https://tradevodata.com/v1/fundamentals",
    params={"ticker": "AAPL", "as_of": "2024-06-30"},
    headers={"x-api-key": "tvd_your_key_here"},
)
for f in r.json()["fundamentals"]:
    print(f["concept"], f["value"], "known since", f["filed"])

R

library(httr)

r <- GET(
  "https://tradevodata.com/v1/fundamentals",
  query = list(ticker = "AAPL", as_of = "2024-06-30"),
  add_headers(`x-api-key` = "tvd_your_key_here")
)
for (f in content(r)$fundamentals) {
  cat(f$concept, f$value, "known since", f$filed, "\n")
}

Errors

400
Missing/invalid ticker, as_of, or concept. The body explains which.
401
Missing, invalid, or deactivated API key.
404
Ticker not in the universe (5,193 US companies with 10-K data).
429 · requests
Daily request quota (5,000/day) exceeded. Resets 00:00 UTC — the Retry-After header says how many seconds until then.
429 · tickers
Daily distinct-ticker limit (2,500/day) reached. Same reset, same Retry-After header. The error body says which limit you hit.
500
Our fault. If it persists, email support.

An empty result with count: 0 and a note means the ticker exists but nothing had been filed on or before your as_of.

GET/v1/health

Public liveness check (no key needed): returns { ok, rows, db_ms }.

Limits

Two independent daily limits per key. Both reset at 00:00 UTC, and every 429 carries a Retry-After header with the seconds until reset.

5,000 requests
Total requests per day. Sized so a normal research workflow never notices it.
2,500 tickers
Distinct tickers per day. Repeat calls for the same ticker are free — only the first request for each new ticker counts.

The honest why: the dataset is the product. A real backtest touches a few hundred names; a full-universe scrape is all 5,193. The ticker cap sits between those two on purpose — it's an anti-bulk-extraction wall, not a revenue lever. For whole-universe work, don't fight the cap — use the bulk endpoints below (included in your plan).

Bulk & cross-section

For whole-universe work — cross-sectional screens, factor backtests — don't loop the per-ticker endpoint. Both are included in your $49/mo plan.

GET /v1/download
The entire point-in-time dataset as one gzipped CSV (313,001 rows). Costs 1 request, capped at 3 downloads/day. Verify integrity against the x-dataset-sha256 response header.
GET /v1/snapshot?as_of=YYYY-MM-DD
Every ticker's latest value that was public on or before as_of — the whole-universe cross-section for one rebalance date. Optional &concept=. Costs 25 requests (the workload of thousands of per-ticker calls).
curl -H "x-api-key: tvd_..." https://tradevodata.com/v1/download -o tradevodata.csv.gz
curl -H "x-api-key: tvd_..." "https://tradevodata.com/v1/snapshot?as_of=2024-06-30&concept=Revenue"

Coverage & semantics