EDI Analytics API
Programmatic access to the same engines that power the EDI platform: security resolution across the US, Brazil, Mexico and Peru; end-of-day global prices; standardized fundamentals with fiscal-period intelligence; cross-domain as-of matrices; and asynchronous batch execution. One request produces the same number in Excel, Python, the Quant Workstation, and a scheduled report — parity is structural, not tested-for.
Quickstart
Authenticate once — the session cookie carries your entitlements on every subsequent call.
# 1. Sign in (stores the session cookie) curl -c cookies.txt -X POST https://api.edi.finance/api/auth/login \ -H "Content-Type: application/json" \ -d '{"email": "you@yourfirm.com", "password": "…"}' # 2. Resolve identifiers to canonical entity ids curl -b cookies.txt -X POST https://api.edi.finance/api/analytics/v1/resolve \ -H "Content-Type: application/json" \ -d '{"identifiers": [{"value": "PETR4-BR"}, {"value": "AAPL"}]}' # 3. A cross-market comps grid in USD, one call curl -b cookies.txt -X POST https://api.edi.finance/api/analytics/v1/matrix \ -H "Content-Type: application/json" \ -d '{"ids": ["AAPL-US","BBAS3-BR","BIMBOA-MX"], "metrics": ["close","MARKET_CAP","SECTOR"], "asOf": "2026-07-30", "currency": "USD"}'
Or with the Python reference client:
from edi_analytics import EdiAnalyticsClient, spill_matrix client = EdiAnalyticsClient("https://api.edi.finance") client.login("you@yourfirm.com", "…") env = client.matrix(ids=["AAPL-US", "BBAS3-BR"], metrics=["close", "MARKET_CAP", "SECTOR"]) grid = spill_matrix(env, ids=["AAPL-US", "BBAS3-BR"], metrics=["close", "MARKET_CAP", "SECTOR"])
Service authentication — API keys
For scripts, SDKs, and server-to-server integrations, create an API key (it acts as you, with
your entitlements) and send it as X-API-Key or Authorization: Bearer —
no cookies involved. Keys are shown once at creation, can expire, and are revocable; managing
keys always requires a signed-in session, never a key.
# Create a key (session-authenticated), then use it anywhere curl -b cookies.txt -X POST https://api.edi.finance/api/analytics/v1/keys \ -H "Content-Type: application/json" -d '{"label": "research pipeline", "expiresInDays": 365}' curl -H "X-API-Key: edi_sk_…" -X POST https://api.edi.finance/api/analytics/v1/resolve \ -H "Content-Type: application/json" -d '{"identifiers": [{"value": "AAPL"}]}'
Python: EdiAnalyticsClient("https://api.edi.finance", api_key="edi_sk_…") — skip
login() entirely.
Core concepts
The envelope
Every data endpoint returns the same three-part envelope. Rows in data are flat and
self-describing — they serialize identically to JSON, CSV, and Excel spill ranges.
{
"data": [ /* flat observation rows */ ],
"errors": [ /* row-level failures — never a request failure */ ],
"meta": { "pagination": { "total": 412 }, "registryVersion": "2026.07.16.1A" }
}Identifiers and the requestId echo
Every endpoint accepts mixed identifier types directly — market-qualified tickers
(PETR4-BR, BIMBOA-MX), bare tickers, ISINs, CUSIPs, CIKs, and canonical
entity ids. Every response row carries both requestId (your identifier, exactly as
sent — the join key back to your spreadsheet cell or dataframe index) and entityId
(our canonical key, stable across ticker changes and share classes).
Partial failure is the norm
One bad ticker must never kill a 500-cell workbook. Unresolvable identifiers, unknown metrics,
and unpriceable conversions come back as entries in errors — each with a
requestId, a stable code, and a human-readable detail —
while every other row returns normally.
Provenance
Every response states the metric-registry version that priced it
(meta.registryVersion), and rows carry the currency actually returned. Numbers are
auditable across clients: the same request produces the same value everywhere, by construction.
Vocabulary
The request parameters below mean the same thing on every endpoint that accepts them.
Periodicity (fundamentals)
| Value | Meaning |
|---|---|
FY | Fiscal years, as reported. |
QTR | True fiscal quarters. 10-Qs stored as year-to-date are de-cumulated server-side; Q4 derives from the fiscal-year total when unreported. |
LTM | Rolling sum of the last four quarters at each quarter-end (flows). Point-in-time items return their value as of that quarter-end. |
YTD | Cumulative within each fiscal year. |
SEMI | Staged — awaiting semi-annual normalization. |
basis selects the restatement axis: original (default) or
restated (staged — awaits vintage modeling).
Frequency (prices & series)
| Value | Observations kept |
|---|---|
D / AD | Every observed trading day. |
W · M · CQ · CSA · CY | Last trading day of each ISO week / calendar month / quarter / half / year. |
AM · AQ · ASA · AY | Anchored: stepping from your startDate (a June 16 start yields Jun 16, Jul 16, …), taking the latest observation at or before each anchor. |
Price adjustment
| Value | Meaning |
|---|---|
SPLIT | Split-adjusted (default; matches stored venue closes). US listings only — MX/BR/PE store the adjusted chain separately and return a row error naming DIV_SPIN_SPLITS. |
DIV_SPIN_SPLITS | Dividend-, spinoff- and split-adjusted. |
UNSPLIT · SPLIT_SPINOFF | Staged — need adjustment-factor history. |
Currency
currency defaults to LOCAL: values return in their native quote or
reporting currency, stated per row — a Brazilian workbook never silently receives USD. Any
ISO code (USD, BRL, MXN, EUR, …) converts
server-side; see currency conversion for which rate applies where.
Security resolution
/api/analytics/v1/resolveThe identity layer under every other call. Send up to 2,000 identifiers of mixed types; get
back canonical entities with company-vs-listing structure. Rows sharing an entityId
are share classes / listings of the same issuer, enumerated under listings.
// POST /api/analytics/v1/resolve { "identifiers": [ {"value": "PETR4-BR"}, {"value": "US0378331005"}, {"value": "037833100", "type": "CUSIP"} ] } // 200 — one row per candidate { "data": [ { "requestId": "PETR4-BR", "entityId": "BR-9512", "ticker": "PETR4", "market": "BR", "matchedBy": "ticker", "candidateCount": 1, "listings": [ {"ticker": "PETR3", "market": "BR"}, {"ticker": "PETR4", "market": "BR"} ] } ] }
- Bare tickers resolve against your default market, else US. Qualify with a suffix (
-BR,-MX,-PE,-US) to be explicit; hyphenated tickers likeBRK-Bare left intact. candidateCount > 1marks an ambiguous identifier — data endpoints report these as row errors rather than guessing.- Identifier shape is auto-classified; pass
type(TICKER·ISIN·CUSIP·CIK·ENTITY_ID) to override.
Metric catalog
/api/analytics/v1/catalog/metricsThe authoritative registry of every metric the engines can price — the same registry the platform's screener, quant panels, and formula workbench execute against, including your organization's custom formula metrics. Autocomplete from here and a request can never name a metric the engines don't know.
| Filter | Meaning |
|---|---|
dataset | financials_standardized · prices · valuation · derived · returns · entity_attributes · … |
search | Matches code, name, and aliases (?search=revenue). |
valueType | currency · percent · ratio · price · shares · text · … |
entityType | company · etf · fund · bond · manager |
includeDeprecated | Deprecated metrics carry a replacementMetricCode. |
kind | line (a statement line as filed or standardized) · amount (a computed currency amount) · ratio · market · attribute · holdings. |
industryProfile | industrial · bank · insurance · reit · fund: only the metrics that exist for that issuer type. Loans / Deposits is a bank metric; the current ratio is not. |
market | US · MX · BR · PE · CL: cross-market metrics plus the ones filed in that market (as-reported lines, institutional holdings, securities lending). |
limit / offset | Pagination; meta.pagination.total is the filtered count. |
Each row states its dataset, engine, value type, unit, supported entity types, time types
(as-of / time-series), period modes, and status, and its place in the metric taxonomy:
folderPath (statement lines under Financial statements, indicators under
Ratios and indicators, one folder per ratio family, as-reported lines by regulator),
kind, basis (standardized · as_reported ·
derived · observed), statement, market,
industryProfiles (empty means every issuer type) and canonicalDataset,
set on a copy of a code that another dataset owns. Names carry Spanish and Portuguese aliases,
so ?search=deuda%20neta finds NET_DEBT.
Global prices
/api/content/global-prices/v1/pricesEnd-of-day OHLCV across all covered markets, with cross-listing containment built in: a São Paulo line sharing a ticker with a US issue can never splice into the US series. Duplicate observations are deduplicated to one canonical row per trading day.
// POST /api/content/global-prices/v1/prices { "ids": ["BBAS3-BR"], "startDate": "2026-01-01", "endDate": "2026-07-30", "frequency": "M", "fields": ["price", "volume"] } // 200 — month-end observations, native currency stated per row { "data": [ { "requestId": "BBAS3-BR", "entityId": "BR-1023", "date": "2026-06-30", "currency": "BRL", "price": 19.91, "volume": 31200400 }, … ] }
fields:price·priceOpen·priceHigh·priceLow·volume.adjustselects the price basis.- All eleven frequency values are supported, calendar-bucketed and start-date-anchored.
- With a
currency, each observation converts at that date's spot rate; volume never converts.
Fundamentals
/api/content/fundamentals/v1/fundamentalsStandardized fiscal data with the platform's period intelligence. The server owns period semantics — year-to-date storage, fiscal-year-end inference, Q4 derivation, LTM windows — so a formula never has to.
// POST /api/content/fundamentals/v1/fundamentals { "ids": ["AAPL-US"], "metrics": ["REVENUE"], "periodicity": "LTM", "fiscalPeriod": { "start": "2025-01-01", "end": "2026-07-30" } } // 200 — rolling last-twelve-months revenue at each fiscal quarter-end { "data": [ { "requestId": "AAPL-US", "metric": "REVENUE", "periodicity": "LTM", "fiscalYear": 2026, "fiscalPeriod": "Q2", "fiscalEndDate": "2026-03-28", "currency": "USD", "value": 451442000000 }, … ] }
fiscalPeriod.start/endare calendar dates; the server resolves them to completed fiscal periods. Default window: five years back.- Rows report the issuer's reporting currency, or your requested
currencyafter conversion. - Unknown metrics are per-metric row errors; the rest of the request still returns.
Statement taxonomies, whole statements, and fiscal periods
Three discovery-and-retrieval companions to the fundamentals endpoint:
| Endpoint | Returns |
|---|---|
GET /taxonomies?market=BR&template=banks | The chart of accounts actually reported by that market and format — market: US·BR·MX·PE, template: industrials·banks·insurance, optional statement. Each row carries the account code, label, statement, and how many companies report it — so you know the available accounts before querying data. A BR Bancos balance sheet (Interbank assets, Loans…) is not a US Banks one. |
POST /statements | The entire statement (IS, BS, CF, or FULL) for each id in one call, at the period implied by asOf — mode: "annual" (latest fiscal year, default) or "latest" (latest filed period, values as filed). Currency conversion is statement-aware: balance sheet at period-end spot, flows at period-average. |
POST /segments | Business, product, and geographic revenue breakdowns for US filers, from structured filings: the issuer's own segment names, values as filed, eliminations excluded. dimension: business·product·geographic; any catalog metric. |
POST /periods | Each company's reported fiscal periods (year, period, dates, account count) — coverage before you fetch. |
// POST /api/content/fundamentals/v1/statements — one call, whole statement { "ids": ["BBAS3-BR"], "statement": "FULL", "asOf": "2026-07-30", "currency": "USD" }
Ratios
POST /ratios — the ratio catalog as a
series: one tab of it (group), or named ratios from any tab, with every
flow leg read at one period basis. This is the web application's Ratios window over the wire —
the same engine call, so a number here and a number there cannot disagree. The
matrix still serves any ratio at a single as-of date; this endpoint is
for the history.
// POST /api/content/fundamentals/v1/ratios { "ids": ["AAPL-US", "BIMBOA-MX"], "ratios": ["GROSS_MARGIN", "ROE", "PB"], "periodBasis": "ltm", "scale": "quarterly" } // 200 — one row per (id, ratio, period); a flow ratio carries the basis it was read at { "data": [ { "requestId": "AAPL-US", "ratio": "GROSS_MARGIN", "group": "profitability", "period": "FY2026 Q3", "periodEnd": "2026-06-27", "value": 0.4865, "valueType": "percent", "periodBasis": "ltm", "scale": "quarterly" }, { "requestId": "AAPL-US", "ratio": "PB", "group": "valuation", "period": "2026-08-31", "periodEnd": "2026-08-31", "value": 43.05, "valueType": "multiple", "periodBasis": null, "scale": "monthly", "scaleClampReason": "tab_grid" }, … ], "errors": [] }
| Parameter | Meaning |
|---|---|
group | valuation · profitability · returns · leverage · liquidity · efficiency · cash_flow · per_share · growth. Omit it when ratios names the codes: a ratio's tab is a property of the ratio, and a request mixing tabs is answered tab by tab. |
periodBasis | ltm (default) · ytd · 3m · fy — how every flow leg (revenue, EBITDA, cash flow) is read. Balance-sheet ratios are point-in-time and do not move with it; their rows carry periodBasis: null so a reader knows why the row sat still. |
scale | quarterly (default) · annual for the fundamental tabs; daily · weekly · monthly for valuation, whose numerator moves every trading day and has no fiscal period. Growth is annual only. The scale is clamped to what the tab and the issuer can honour, and every row says so in scaleClampReason: tab_grid, or issuer_reports_annually for the issuers that file no interim statements. |
asOf, start | Reference date (default today) and how far back to reach; without start the span is the scale's own — ten years of quarters, fifteen of fiscal years, two of days. |
currency | Converts the currency-magnitude rows only (Total debt, Market cap, per-share amounts); a ratio is never converted. |
periodis a fiscal bucket (FY2026 Q3) on the fiscal grids and an ISO date on the price grid.periodEnddates every row, because buckets are not comparable across issuers: Apple's FY2026 Q3 ended 2026-06-27 and a December year-end's ended 2026-09-30.valueType: "percent"is a fraction — a 48.65% gross margin is0.4865, the same number the matrix and the catalog give for the same code.- Withheld is not missing. A bank's gross margin and an industrial's loans-to-deposits come back as
NO_DATArow errors that say why; every (id, ratio) you ask for yields a value or an error, never a silently absent cell. - GET
/ratios/fieldslists every ratio the endpoint serves with its tab, formula, value type, grid scales and the issuer types it exists for — read it before building a query.
Point-in-time
POST /point-in-time — fundamentals as they were
known on a date, for backtesting without lookahead bias (US filers). Two guarantees: a fiscal
period is invisible until its filing's publication date (Apple's December quarter does not exist
before January 30), and when a figure is later restated, the value that was current at
pitDate is served — rows carry firstPublished, lastPublished
(amendments), and valueVintage (as_known when a captured vintage applies;
restatement capture accrues from 2026-08-02 forward). pitDataItems=true on the catalog
lists PIT-capable metrics. FY and QTR; compose LTM client-side from PIT quarters.
Matrix
/api/analytics/v1/matrixMany securities × many metrics at one as-of date, in one request — the comps-grid primitive. Metrics can mix datasets freely: prices, fundamentals, valuation ratios, derived measures, and text attributes all return through the same envelope.
// POST /api/analytics/v1/matrix { "ids": ["AAPL-US", "BBAS3-BR", "BIMBOA-MX"], "metrics": ["close", "MARKET_CAP", "PE_LTM", "SECTOR"], "asOf": "2026-07-30", "currency": "USD" } // 200 — one cell per (id, metric); as-of = latest observation on or before { "data": [ { "requestId": "AAPL-US", "metric": "MARKET_CAP", "dataset": "derived", "valueType": "currency", "value": 4925847973597 }, { "requestId": "BBAS3-BR", "metric": "close", "dataset": "prices", "value": 4.11 }, { "requestId": "BIMBOA-MX", "metric": "SECTOR", "dataset": "entity_attributes", "valueType": "text", "value": "Food & Beverage" } ] }
- Metric names colliding across datasets resolve by a documented precedence — reported financials win. Pass an explicit
{"metric": "…", "dataset": "…"}pair to override;GET /catalog/metricslists which datasets serve a given name. - Financials metrics accept
periodMode: "fy"for latest-fiscal-year values; use the fundamentals endpoint for QTR/LTM/YTD series of statement lines, the ratios endpoint for ratio series at a chosen period basis, or precomputed valuation metrics (PE_LTM,EV_EBITDA_LTM, …). - Cells that compute to null are absent, not errors — a spreadsheet fills
#N/A.
Screener
/api/content/screener/v1/searchRun screens on the same engine as the platform screener — conditions on financial metrics,
price metrics, valuation ratios, and your organization's custom formula metrics, with sector,
exchange, country, and universe filters. Companion endpoints:
POST /count returns the match count without
materializing rows (wire it to a live "N companies match" indicator), and
GET /fields lists every screenable field.
// POST /api/content/screener/v1/search { "conditions": [ { "metric": "MARKET_CAP", "operator": "gt", "value": 100000000000 }, { "metric": "ROE", "operator": "gte", "value": 0.15 } ], "metrics": ["REVENUE", "PE"], "sortBy": "MARKET_CAP", "currency": "USD", "limit": 50 } // 200 — one row per matching security; meta.pagination.total = full match count { "data": [ { "ticker": "AAPL", "entityId": "0000320193", "sector": "Technology", "nativeCurrency": "USD", "marketCap": 4925847973597, "metrics": { "REVENUE": 451442000000, "PE": 43.9 } }, … ] }
- Operators:
gt·gte·lt·lte·eq·neq. A company missing a conditioned metric fails the screen. currencydefaults to USD here (not LOCAL): screening thresholds compare across issuers, which requires one common currency. Rows still state theirnativeCurrency;meta.fxRateDatediscloses the rate date used.tickersrestricts the candidate universe;batch:"Y"queues large screens through batch execution.- Delisted listings are excluded by default. Omitting
excludeStatusapplies the platform's reading of "not known to be dead": listings whose status iscancelled/canceladoare dropped. Thousands of delisted US issuers carry their final fundamentals (for point-in-time work), and a ratio screen would otherwise list companies dead since 2012 — a trade-recency test would not catch them, since many still print pennies over the counter. PassexcludeStatus: []to include them, orstatusto select by status explicitly;tradedWithinDaysremains the separate, opt-in liveness test.
Point-in-time screening
POST /api/content/screener/v1/point-in-time — run a screen as it would have run
on a past date. The live screener is present-tense; running it with old thresholds tells you
which of today's survivors would pass today.
// POST /api/content/screener/v1/point-in-time { "asOf": "2019-06-28", "conditions": [ { "metric": "REVENUE", "operator": "gt", "value": 20000000000 }, { "metric": "ROE", "operator": "gt", "value": 0.25 } ], "metrics": [ "REVENUE", "ROE" ], "sortBy": "REVENUE" } // 200 — each row states the filing it was judged on { "data": [ { "ticker": "ORCL", "fiscalYear": 2019, "fundamentalsPublished": "2019-06-21", "availabilityBasis": "sec_filed", "reportingCurrency": "USD", "metrics": { "REVENUE": 39506000000, "ROE": 0.51 } } ] }
- Every row carries
fiscalYear,fundamentalsPeriodEnd,fundamentalsPublishedandavailabilityBasis, so a screen is reproducible: you can see exactly which filing each name was judged on. tradedWithinDaysdefines liveness. It has a default of 10, and widening it admits thinly-traded names a narrow window drops.currencydefaults to USD and converts at the as-of date's spot rate, not today's — converting a 2019 screen at a 2026 rate would be a lookahead leak through the currency rather than the fundamentals. Ratios are never converted.- An issuer whose reporting currency cannot be priced on that date is excluded and reported
as an
FX_UNAVAILABLErow error, never compared on a different basis.
MARKET_CAP,
PE, PB, PS, EV and the rest return a row
error rather than a number. Forming a historical market cap needs the price and the share count
on the same split basis, and the stored price history is not on one: part is as-traded and part
is adjusted as of a per-ticker refresh date. The result would be correct for some issuers and
wrong by the split ratio for others, with nothing in the output to say which. Fundamentals,
margins, growth, returns on capital and leverage are unaffected.Funds
/api/content/funds/v1/…Local-market fund data across Brazil (65,000+ registered funds), Mexico, and Peru, with the
identifier conventions each market actually uses: BR funds resolve by CNPJ in any format
(00.000.684/0001-21 or 00000684000121), MX and PE funds by ticker or
entity id. Four endpoints share one contract:
| Endpoint | Returns |
|---|---|
POST /summary | Registry attributes: name, classification, operator/administrator, benchmark, status, native currency. |
POST /prices | NAV series (fields: ["nav", "netAssets"]) with the full frequency vocabulary. |
POST /returns | Periodic returns computed from NAV at the sampled frequency. Fund NAVs are accumulation-style, so these are total returns. |
POST /flows | Daily inflows / outflows / net flow — BR only, where the market reports daily; other markets return a row error, never silence. |
POST /holdings | Portfolio holdings for BR/MX/PE funds and US ETFs at the latest report on or before asOf: name, identifiers, asset class, quantity, market value, weight. top caps rows (default 50). |
Institutional equity holdings live at
/api/content/ownership/v1: POST /holders
returns who holds a security (per manager: latest filing for the target quarter wins — amendments
supersede), and POST /manager-holdings returns a
manager's portfolio by filer CIK. The full institutional filer universe (~9,000 managers) is
covered for every fully-due quarter; because these disclosures are due 45 days after quarter
end, holder lists default to
the latest fully-due quarter — pass includePartial: true during a filing
window to see the in-progress quarter. Values are USD; weights are percent of reported value.
// POST /api/content/funds/v1/prices { "ids": ["00000684000121", "+TASAD1F1"], "fields": ["nav", "netAssets"], "startDate": "2026-01-01", "endDate": "2026-07-30", "frequency": "M", "currency": "USD" }
- BR series are the fund's main share class; PE funds carry per-issue currency (PEN or USD), stated per row.
currencyconverts monetary fields (NAV, net assets, flows) at each observation's spot rate, exactly as in global prices.
Reference data
Point-in-time identity and the filing record behind it. Both answer the same question in different registers: what did this identifier mean then — the join key any historical dataset outside our universe needs.
Identifier history
POST /api/content/reference/v1/identifier-history — every ticker, ISIN, CUSIP,
CIK, exchange and MIC a security has carried, with validity intervals, back to 1926. Rows carry
the corporate-action code behind each change, so a rename can be told apart from a merger.
asOf returns the intervals open on that date.// POST /api/content/reference/v1/identifier-history { "ids": [ "AAPL" ], "asOf": "2019-06-28" } // 200 — what each identifier was on that date { "data": [ { "requestId": "AAPL", "identifierType": "ISIN", "identifierValue": "US0378331005", "validFrom": null, "validTo": null, "isCurrent": true } ] }
identifierTypesfilters toTICKER·ISIN·CUSIP·CIK·EXCHANGE·MIC·MIC_SEGMENT·SECURITY_DESCRIPTION— the same words/resolveaccepts, so a returned value can be sent straight back in.startDate/endDateselect intervals open at any point in the window, not intervals that began inside it.- Sourced from the EDI security master: US listings. Anything else returns a row error rather than an empty result, which would be indistinguishable from "never changed".
Filings index
POST /api/content/reference/v1/filings — 2.2M US regulatory filings across 20,205
registrants since 1993: form type, filing date, period end, accession number, amendment type and
the document URL. The event clock for announcement-driven work, and the audit trail behind every
fundamental we serve.
formTypes matches by prefix, so 10-K includes 10-K/A —
an amendment is the same event refiled, and it is usually the restatement you came for.
Corporate-action adjustment factors
/corporate-actions now returns priceFactor,
shareFactor, cashComponent, affectsSplitAdjusted,
affectsTotalReturn and factorStatus alongside the event terms, so you
can rebuild your own adjusted series and reconcile it against ours.
ratioOld: 1, ratioNew: 9 — and the price
factor is 0.1, a 10-for-1. Deriving the multiplier from the ratio gives 9×. Where a
factor could not be computed, the fields are null and factorStatus says why rather
than the row going missing.Trading calendars
POST /api/content/reference/v1/calendar — the sessions each market actually
trades, for US, BR, MX and PE, from the 1990s through 2029. Non-trading days carry the reason;
pass includeNonTrading for a full date spine.
// POST /api/content/reference/v1/calendar { "markets": [ "US", "MX" ], "startDate": "2026-04-01", "endDate": "2026-04-06" } // 200 — 2 April is a session in the US and a holiday in Mexico { "data": [ { "market": "US", "date": "2026-04-02", "isTradingDay": true, "origin": "scheduled" }, { "market": "MX", "date": "2026-04-01", "isTradingDay": true, "origin": "scheduled" } ] }
originisobserved(a session we hold prices for),scheduled(rule-derived, the only kind available for future dates) orboth. A client planning a rebalance two years out can tell which it is looking at.- Where the rules and the tape disagree, the row keeps what was observed and states the
conflict. A national day of mourning is announced, not ruled — it belongs in the calendar as an observed closure, not as a rule nobody can write. sessionflags US early closes (Independence Day eve, the day after Thanksgiving, Christmas Eve). No other market publishes them reliably enough for us to claim the same.
Trading-day arithmetic
POST /api/content/reference/v1/calendar/offset — move N trading days from an
anchor date. T+2 settlement, rebalance scheduling and event windows are the same question, and
all silently wrong when answered with calendar days.
// T+2 from 1 April 2026, across Easter { "markets": [ "US", "MX" ], "dates": [ "2026-04-01" ], "offset": 2 } // 200 — same anchor, different answers: Mexico also closes Maundy Thursday { "data": [ { "market": "US", "resultDate": "2026-04-06" }, { "market": "MX", "resultDate": "2026-04-07" } ] }
- An
offsetof0snaps to the session in force on or before the anchor — what an as-of date means when it lands on a holiday. - Where the calendar runs out before the offset does,
resultDateis null andtradingDaysAvailablesays how far it reached. Clamping to the edge would return a plausible, wrong date.
Insider transactions
POST /api/content/ownership/v1/insider-transactions — 1.75M insider transaction rows
since 2004, with the reporter's relationship to the issuer (director, officer, 10% owner) joined in.
Two distinctions the raw filings do not make, and that a caller would otherwise have to rebuild:
- Not every code is a trade. The filing puts open-market purchases and sales (P/S) in the
same table as grants (A), option exercises (M/X) and tax withholding (F). Summing across them
produces a number that reads like trading activity and is not — so rows carry an
activityclassification, and the endpoint defaults to open-market only. - Derivative rows are different objects. Adding derivative and non-derivative share
counts double-counts an exercise against the transaction it settles.
securityBucketis explicit on every row and filterable.
Fixed income
POST /api/content/fixed-income/v1/search ·
/reference · /prices — Mexican paper, Peruvian
corporates and Brazilian debentures. 2.2M price observations; a bond universe
is discovered rather than known by ticker, so start with search.
indexBasis
reads "DI + 1,6%" and spreadOverIndex carries the
indicative spread over it. yieldToMaturity is null
on a Brazilian row, because 0.74 sitting in that column next to Mexico's 6.52
says Brazilian credit yields a seventh of Mexican rather than DI plus 0.74.- Durations are years everywhere. Brazil publishes dias úteis, converted at 252 — the DI curve's own divisor.
- Percent of par is served only where the source publishes it (PE, BR).
Mexico carries 114 distinct par values, so a ratio derived from its
master would be a normalization nobody can stand behind;
parValuerides on the reference row instead. - Nothing is FX-converted: a yield is a rate and a duration is time. Rows state their own currency.
- Ratings come back as a map keyed by agency — six rate Mexican paper and they disagree.
Fund fees
POST /api/content/funds/v1/fees — expense ratios, management
and performance fees, loads and turnover. US share classes from prospectus
disclosures (7.58M facts over 64,931 classes), Brazilian funds from their own
regulatory fund disclosures.
grossExpenseRatioandnetExpenseRatioare never collapsed into one number: the difference is a waiver, and waivers expire.- Every field on a row comes from one document, whose date is on the row. Reading the newest value per field independently produced fee schedules no prospectus ever stated.
asOfselects the newest disclosure published on or before that date.
Ownership changes
POST /api/content/ownership/v1/changes — what institutions did
in a security between two quarters: who entered, added, trimmed or left,
ordered by what actually moved.
EXITED only when the manager actually filed for that
quarter — one who filed nothing is NOT_FILED.- Both books are assembled through the platform's institutional-holdings authority: a NEW HOLDINGS amendment is additive, not superseding, so a book is the base filing plus every additive amendment after it. Read as a replacement, one manager's book came out at $47.0bn against a true $4,042.9bn.
- Actions:
NEW·ADDED·TRIMMED·EXITED·NOT_FILED·UNCHANGED, filterable.
Research
The academic research surface: point-in-time panels over survivorship-safe historical universes, pinned by citable Research Snapshots. Every dataset answer leads with its diagnostics — universe coverage, PIT withholding, empty metrics — because a panel looks equally complete at 6% and 96% coverage without them. Methods are documented in the twelve methodology chapters shipped with the academic package (identity, PIT, universes, adjustment, event studies, snapshots, and more).
Access. Same authentication as every endpoint (API key or session). While the Academia section is in staging, research endpoints additionally require the research role on your account.
Downloads — the academic package
Self-hosted and versioned with the platform: the edifinance SDK wheel, the teaching notebooks in three languages (English, Spanish, Portuguese — Excel sample workbooks included), and the twelve methodology chapters. The SDK is not on PyPI while Academia is in staging — install it straight from here (works on Colab too).
# the SDK is not on PyPI yet — install from the platform
pip install https://api.edi.finance/static/academic/edifinance-0.1.1-py3-none-any.whl- edi-academic-package.zip — everything: SDK wheel, notebooks (en/es/pt), methodology chapters, Excel workbooks.
- edifinance-0.1.1-py3-none-any.whl — the Python SDK alone.
- notebooks/README.md — the notebook inventory; individual files under
/static/academic/notebooks/.
Historical universes
/api/research/universesUniverse definitions with snapshot counts. Resolving one as of a date yields an
immutable membership snapshot: status is stored as of that date (a security
delisted in 2022 is active in a 2019 snapshot), and unpriced members are
flagged price_coverage: none, never dropped — dropping them is the
survivorship bias this engine exists to prevent.
# memberships as of a date, grid-shaped
curl -s "https://api.edi.finance/api/research/excel/universe?code=BR_ALL_EQUITY&as_of=2022-12-31" \
-H "X-API-Key: $EDI_KEY"Dataset builder — PIT panels
/api/research/dataset-buildBuilds a panel from a named definition (create one via
POST /api/research/dataset-definitions) and returns a capped preview plus
full-panel diagnostics. In PIT mode, a fact whose availability cannot be proven is
withheld and counted — never served on a guess. PIT coverage is market-honest:
US and Brazil are fully supported; Mexico is partial; Peru is not supported.
# SDK install: see Downloads above (not on PyPI yet) from edifinance import ResearchClient rc = ResearchClient(api_key="…") df = rc.panel(universe="BR_ALL_EQUITY", metrics=["REVENUE", "NET_INCOME"], start="2019-12-31", end="2023-12-31", point_in_time=True) df.attrs["diagnostics"] # coverage, withheld facts, snapshot code
- Definitions are named and reusable; the SDK derives deterministic names so identical calls share one definition.
- Pass a
snapshot_labelto mint a citable Research Snapshot for the run. - Excel:
=EDI.DATASET(…)spills the same panel with a provenance line as row 1.
Research Snapshots
/api/research/research-snapshotsA snapshot (EDI-RS-<year>-<token>) pins everything needed to recreate an
analysis: knowledge date, the frozen universe snapshot, metric/factor registry
content hashes, currency and adjustment policies, the request payload, and an
order-independent dataset hash. Published snapshots are immutable by database trigger —
a citation resolves forever. Provenance for one code:
GET /api/research/excel/snapshot/{code} (also =EDI.SNAPSHOT(code) in Excel).
Event studies
/api/research/event-studyRuns a seeded study: market-model or market-adjusted abnormal returns, AAR/CAAR with a cross-sectional t-test, trading-day windows, event dates mapped forward to the next session (US filing events are timed by regulatory acceptance timestamps — two-thirds of filings land after the close and belong to the next session). Dropped events are counted by reason. The engine's standing validation is a placebo test: random dates must find nothing.
curl -s -X POST https://api.edi.finance/api/research/event-study \
-H "X-API-Key: $EDI_KEY" -H "Content-Type: application/json" \
-d '{"definition_name": "us_earnings_announcements"}'Batch execution
Every data endpoint accepts "batch": "Y" with the identical request body —
flip one flag when a workbook grows past the synchronous limits. The batch branch runs the same
code path as the synchronous one, so results are identical by construction.
| Step | Call | Response |
|---|---|---|
| 1. Submit | POST any data endpoint with "batch":"Y" | 202 + Location header; body carries the job id |
| 2. Poll | GET /api/analytics/v1/batch-status?id=… | 202 while running · 201 when done (+Location) · 200 with status:"error" on failure |
| 3. Fetch | GET /api/analytics/v1/batch-result?id=… | 200 with the standard envelope |
Jobs are owned by the submitting account and expire after 24 hours. Failed jobs deliver an
EXECUTION_ERROR envelope through the same contract.
Bulk export
Batch execution scales a workbook. Bulk export is for the other shape of problem: loading a dataset into your own store, then keeping it current. One job, one compressed file, and an exact cursor to resume from.
| Step | Call | Response |
|---|---|---|
| 0. Discover | GET /api/content/bulk/v1/datasets | Exportable datasets, their exact column projection, and whether each supports incremental pulls |
| 1. Submit | POST /api/content/bulk/v1/export | 202 + Location; body carries the job id |
| 2. Poll | GET /api/content/bulk/v1/status?id=… | Job row with status, rowCount, byteCount, nextChangedSince |
| 3. Download | GET /api/content/bulk/v1/download?id=… | The file — gzipped CSV or NDJSON |
# Seed: the full price history, one file curl -b cookies.txt -X POST https://api.edi.finance/api/content/bulk/v1/export \ -H "Content-Type: application/json" -d '{"dataset": "prices_eod"}' # Every night after that: only what has been written since the last pull curl -b cookies.txt -X POST https://api.edi.finance/api/content/bulk/v1/export \ -H "Content-Type: application/json" \ -d '{"dataset": "prices_eod", "changedSince": "2026-08-21T23:16:18.915379Z"}'
nextChangedSince is the contract. A completed job reports the
highest source write timestamp it actually included. Pass that value back verbatim and the next
pull resumes exactly where this one stopped — nothing written mid-run is missed or repeated.
Using your own clock, or now, silently loses rows.- Nine datasets today:
prices_eod,corporate_actions,corporate_action_factors,identifier_history,filings_index,insider_transactions,fundamentals_standardized,index_levels,fund_monthly_returns. - A date window (
startDate/endDate) andchangedSinceare alternatives, never both: one selects a period, the other selects what has changed. - Datasets with no source write timestamp reject
changedSinceby name rather than returning everything each time, which would look like a working delta feed. - Columns are an explicit projection per dataset, so a column added internally never appears unannounced in your warehouse. Files expire after 48 hours.
- Two exports run concurrently across the platform; a third returns
429withRetry-After.
Dataset status
GET /api/analytics/v1/datasets — for each dataset: the coverage window, when it
last loaded, row and entity counts, which endpoints serve it, and a freshness verdict.
// GET /api/analytics/v1/datasets?dataset=prices_eod { "data": [ { "datasetId": "prices_eod", "cadence": "daily", "firstObservation": "1991-12-02", "lastObservation": "2026-08-21", "lastLoadedAt": "2026-08-21T23:16:18Z", "freshness": "FRESH", "observationLagBusinessDays": 0, "expectedLagBusinessDays": 2, "rowCount": 21768648, "rowCountIsEstimated": true, "computedAt": "2026-08-23T13:52:25Z" } ] }
lastObservationis the latest business date in the data;lastLoadedAtis the latest write. They answer different questions — a loader that ran an hour ago and wrote nothing new is a different failure from one that has not run in a week — so they are separate fields, andlastLoadedScopestates what the write probe measured.freshnessisFRESH·LAGGING·STALE·UNKNOWN, judged against a per-datasetexpectedLagBusinessDays. One threshold cannot serve both a daily price feed and a quarterly ownership filing, so the raw business-day lag is returned alongside the verdict for your own rule.rowCountIsEstimateddistinguishes a planner estimate from an exact count.computedAtis the refresh that produced the row, so a stalled refresher reports its own age instead of passing off old coverage as current.
Currency conversion
Conversion is server-side and semantics-aware — the right rate kind applies to each value:
| Value kind | Rate applied |
|---|---|
| Price observations | Spot rate on each observation's date. |
| Flow fundamentals (IS/CF) | Period-average rate over the fiscal window. |
| Stock fundamentals (BS) | Spot rate at the fiscal period end. |
| Matrix cells (currency/price typed) | Spot rate at the as-of date. |
| Volume, ratios, percents, text | Never converted. |
Cross rates compose through USD. A conversion that cannot be priced — unknown source currency,
unsupported pair, no rate within the staleness window — is an FX_UNAVAILABLE row
error with the affected rows dropped: you never silently receive unconverted native values.
CSV everywhere. Send Accept: text/csv on any data endpoint and the
envelope's flat rows come back as a CSV file — nested maps flatten to dotted columns, row-level
errors ride along as trailing # comment lines, and registryVersion is
stamped at the end. The fastest path from any endpoint into a spreadsheet or dataframe.
Complete endpoint index
Every operation the service routes, 113 of them, generated from the route table itself. The sections above describe the ones most clients start with; everything here is live and callable.
| Method | Path | Summary |
|---|---|---|
| analytics | ||
| GET | /api/analytics/v1/batch-result | Download a finished batch job's envelope. |
| GET | /api/analytics/v1/batch-status | Status codes: 202 while pending/running, 201 when the result exists (Location points at /batch-result), 200 with status=error. |
| GET | /api/analytics/v1/catalog/equity-presets | Canonical equity presets; metric instances resolve through the catalog. |
| GET | /api/analytics/v1/catalog/fund-presets | Canonical Fund Quant presets resolved against market source capability. |
| GET | /api/analytics/v1/catalog/metrics | The unified metric catalog: every metric this API can serve, with its category, datasets, entity types and value type, plus its place in the Research Library taxonomy (folderPath, kind, basi |
| GET | /api/analytics/v1/datasets | Coverage and freshness per dataset: how far it reaches, when it last moved. |
| GET | /api/analytics/v1/health | Liveness plus a per-store check -- database, facts, prices, FX, batch workers -- so a degraded dependency is visible before it is inferred from empty responses. |
| POST | /api/analytics/v1/inspect | Cell-level provenance for one (id, metric) pair: what the identifier resolved to, which dataset served the value, and which engine owns it. |
| GET | /api/analytics/v1/keys | The caller's API keys and their status. |
| POST | /api/analytics/v1/keys | Issue an API key. |
| DELETE | /api/analytics/v1/keys/{key_id} | Revoke one API key immediately. |
| GET | /api/analytics/v1/keys/{key_id}/usage | Daily usage rollups for one of the caller's keys (session-only). |
| POST | /api/analytics/v1/matrix | Cross-domain retrieval: any set of ids against any set of metrics, each cell routed to the dataset that owns it. |
| POST | /api/analytics/v1/resolve | Resolve tickers, ISINs, CUSIPs, CIKs or entity ids to the platform's canonical security, with the candidates when one is ambiguous. |
| POST | /api/research/dataset-build | Run a build and return a PREVIEW plus the full diagnostics. |
| GET | /api/research/dataset-definitions | Named, reusable panel definitions: universe, metrics, window, frequency, PIT mode, currency. |
| POST | /api/research/dataset-definitions | Create a named panel definition. |
| POST | /api/research/dataset-export | Download a full panel as CSV or XLSX (§67). |
| GET | /api/research/dataset-runs | Recent builds with row counts, dataset hashes, full diagnostics, and any minted snapshot codes. |
| GET | /api/research/event-studies | Seeded event-study definitions: model, benchmark, windows, and event counts. |
| POST | /api/research/event-study | Thin transport over the event-study engine (§82 names this route; the engine predates it). |
| POST | /api/research/excel/panel | The =EDI.DATASET() backend: derive-or-reuse a definition (the SDK's exact naming scheme, "xl_" prefixed so Excel-born definitions are recognisable), build, and answer as a grid. |
| GET | /api/research/excel/snapshot/{snapshot_code} | The =EDI.SNAPSHOT() backend -- the provenance inspector (§36) as a key/value grid: what a citation code pins, and whether it is reproducible, as EVIDENCE fields rather than a bare claim. |
| GET | /api/research/excel/universe | The =EDI.UNIVERSE() backend. |
| GET | /api/research/metrics | Metrics that actually have facts, with their coverage. |
| GET | /api/research/research-snapshots | Research Snapshots: citation code, status, knowledge date, universe, registry hashes, and dataset hash. |
| POST | /api/research/snapshots/{snapshot_code}/replay | One-call snapshot replay (§27): re-run the build a citation pins and return the comparison as evidence. |
| GET | /api/research/universes | Universe definitions with markets, benchmark linkage, and snapshot counts. |
| bulk | ||
| GET | /api/content/bulk/v1/datasets | Which datasets can be exported, with their column projection. |
| GET | /api/content/bulk/v1/download | Stream the finished export file. |
| POST | /api/content/bulk/v1/export | Queue a whole-dataset export; poll status, then download one file. |
| GET | /api/content/bulk/v1/status | Progress of one export, or every export this account has queued. |
| fixed-income | ||
| POST | /api/content/fixed-income/v1/prices | Daily bond observations: price, yield, duration, spread. |
| POST | /api/content/fixed-income/v1/reference | Terms for specific bonds: coupon, maturity, par, amount issued, ratings. |
| POST | /api/content/fixed-income/v1/search | Find bonds across MX, PE, BR and CL by issuer, currency, maturity or ISIN. |
| formula | ||
| GET | /api/content/formula/v1/catalog/metrics | Metrics a formula may reference, with their argument shapes. |
| GET | /api/content/formula/v1/definitions | Formulas visible to the caller: their own, plus anything shared with them. |
| GET | /api/content/formula/v1/definitions/{formula_id} | One formula's current definition. |
| GET | /api/content/formula/v1/definitions/{formula_id}/versions | Every saved version of a formula, newest first. |
| POST | /api/content/formula/v1/execute | Evaluate a formula for a set of securities and dates. |
| POST | /api/content/formula/v1/validate | Parse and type-check an expression without running it. |
| fundamentals | ||
| POST | /api/content/fundamentals/v1/fundamentals | Standardized fundamentals on one fiscal basis: FY, QTR, LTM or YTD. |
| POST | /api/content/fundamentals/v1/periods | Which fiscal periods exist for a security, and their end dates -- the calendar to ask fundamentals questions against. |
| POST | /api/content/fundamentals/v1/point-in-time | Fundamentals as they stood on a past date, resolved through the publication ladder so a restatement cannot leak backwards. |
| POST | /api/content/fundamentals/v1/ratios | Financial ratios as a series: one tab of the ratio catalog, or named ratios, at one period basis (LTM, YTD, 3M, FY) on the fiscal or price grid. |
| GET | /api/content/fundamentals/v1/ratios/fields | Every ratio the ratios endpoint serves: tab, formula, value type, grid scales and the issuer types it exists for. |
| POST | /api/content/fundamentals/v1/segments | Reported business and geographic segments. |
| POST | /api/content/fundamentals/v1/statements | Full financial statements as a hierarchy of line items, standardized or as reported. |
| GET | /api/content/fundamentals/v1/taxonomies | Statement taxonomies available per market, and the line items in each. |
| funds | ||
| POST | /api/content/funds/v1/aum | Assets under management history. |
| POST | /api/content/funds/v1/fees | What a fund costs to hold: expense ratios, fees and loads. |
| POST | /api/content/funds/v1/flows | Net subscriptions and redemptions per period. |
| POST | /api/content/funds/v1/holdings | Disclosed portfolio holdings for a fund at a reporting date. |
| POST | /api/content/funds/v1/portfolio/analytics | Risk and exposure analytics computed over a fund's disclosed holdings. |
| POST | /api/content/funds/v1/prices | Fund NAV and market price history. |
| POST | /api/content/funds/v1/rankings | Peer rankings within a fund category over a chosen window. |
| POST | /api/content/funds/v1/registry | Bulk share-class registry for a market, including point-in-time returns. |
| POST | /api/content/funds/v1/returns | Fund returns as reported and as computed from NAV. |
| POST | /api/content/funds/v1/summary | Identity and headline facts for a fund or share class. |
| fx | ||
| POST | /api/content/fx/v1/average | The average FX rate over a period -- the right basis for translating a flow, where a spot rate is right for a balance. |
| POST | /api/content/fx/v1/history | An FX pair over time. |
| POST | /api/content/fx/v1/rate | A spot FX rate on a date. |
| POST | /api/content/fx/v1/translate | Translate an amount between currencies at a stated date and basis, so the rate used is on the response rather than assumed. |
| global-prices | ||
| POST | /api/content/global-prices/v1/corporate-actions | Splits, dividends and other corporate actions with the price and share factors derived from them. |
| POST | /api/content/global-prices/v1/prices | End-of-day prices across all covered markets. |
| POST | /api/content/global-prices/v1/returns | Period returns over named windows, price or total return. |
| POST | /api/content/global-prices/v1/returns-range | Returns between two explicit dates. |
| POST | /api/content/global-prices/v1/returns/snapshot | Standard return windows for a set of securities in one row each: the performance strip, without a request per window. |
| indices | ||
| POST | /api/content/indices/v1/changes | Additions and deletions between two constituent dates. |
| POST | /api/content/indices/v1/constituents | Index constituents and weights at a date. |
| POST | /api/content/indices/v1/history | An index level series over time. |
| POST | /api/content/indices/v1/levels | Index levels. |
| POST | /api/content/indices/v1/membership | The index memberships a security has held, with entry and exit dates. |
| POST | /api/content/indices/v1/search | Find indices by name, market or provider. |
| POST | /api/content/indices/v1/weight | One security's weight in an index over time. |
| lending | ||
| POST | /api/content/lending/v1/history | Securities-lending history. |
| POST | /api/content/lending/v1/summary | Current securities-lending state for a security: balance, rate and availability where the market publishes them. |
| macro | ||
| POST | /api/content/macro/v1/history | A macro series over time. |
| POST | /api/content/macro/v1/observations | The latest observation of a macro series. |
| POST | /api/content/macro/v1/search | Find macro series by name, country or source. |
| market-analytics | ||
| POST | /api/content/market-analytics/v1/correlation | A correlation matrix across securities over a window. |
| POST | /api/content/market-analytics/v1/performance | Performance statistics against a benchmark, including the risk-free leg where a Sharpe ratio is asked for. |
| POST | /api/content/market-analytics/v1/regression | Regress a security against factors or a benchmark. |
| POST | /api/content/market-analytics/v1/risk | Risk statistics over a return window: volatility, drawdown, VaR, beta. |
| ownership | ||
| POST | /api/content/ownership/v1/changes | What institutions did in a security between two quarters. |
| POST | /api/content/ownership/v1/holders | Institutional holders of a security from 13F, N-PORT and BR/MX/PE local-fund disclosures. |
| POST | /api/content/ownership/v1/insider-transactions | Insider activity, classified so the transaction codes do not have to be. |
| POST | /api/content/ownership/v1/manager-holdings | One manager's disclosed book: every position they reported for a quarter. |
| POST | /api/content/ownership/v1/shareholders | Registered and beneficial shareholders as disclosed to the local regulator, for markets that publish them. |
| peers | ||
| POST | /api/content/peers/v1/search | Canonical peer set for one security (peer_universe.build_peer_universe). |
| portfolio | ||
| POST | /api/content/portfolio/v1/analytics | Risk, exposure and performance analytics for a supplied portfolio of weights or positions. |
| POST | /api/content/portfolio/v1/optimize | Optimize weights under constraints, over the same risk math the Construction workstation uses. |
| POST | /api/content/portfolio/v1/walk-forward | Optimize at each as-of date on what was knowable then, hold the weights out of sample to the next, and chain the held-out segments -- beside 1/N over each step's eligible names and the bench |
| rates | ||
| POST | /api/content/rates/v1/curve | A yield curve: every tenor at one date. |
| POST | /api/content/rates/v1/history | An interest rate over time. |
| POST | /api/content/rates/v1/rate | An interest rate on a date. |
| POST | /api/content/rates/v1/spread | The spread between two tenors on one curve, or across two curves. |
| reference | ||
| POST | /api/content/reference/v1/calendar | Trading sessions per market. |
| POST | /api/content/reference/v1/calendar/offset | Move N trading days from an anchor date. |
| POST | /api/content/reference/v1/filings | The regulatory filing index for a security: what was filed, and when. |
| POST | /api/content/reference/v1/identifier-history | Every identifier a security has carried, with validity intervals. |
| regime | ||
| POST | /api/content/regime/v1/history | The regime classification over time. |
| POST | /api/content/regime/v1/snapshot | Which market regime is in force now, and on what evidence. |
| POST | /api/content/regime/v1/stats | How an asset behaved conditional on regime. |
| screener | ||
| POST | /api/content/screener/v1/count | How many securities match a screen, without returning them. |
| POST | /api/content/screener/v1/distinct-count | Distinct values of one field across a screen -- how many sectors, how many countries -- without pulling the rows. |
| GET | /api/content/screener/v1/fields | Fields available to screen on, with their types and permitted operators. |
| POST | /api/content/screener/v1/point-in-time | Run a screen as it would have run on a past date. |
| POST | /api/content/screener/v1/search | Screen the universe on any catalog metric and return the matching securities with the requested columns. |
| POST | /api/content/screener/v1/statistics | Aggregate statistics over a screen: count, mean, median, percentiles. |
| sustainability | ||
| GET | /api/content/sustainability/v1/fields | Available fields with units, definitions and current issuer coverage. |
| POST | /api/content/sustainability/v1/history | Disclosed history for one measure, one point per reported period. |
| POST | /api/content/sustainability/v1/summary | Latest disclosed workforce or environmental measure for an issuer. |
Error taxonomy
| Code | Meaning | Excel mapping |
|---|---|---|
UNRESOLVED_IDENTIFIER | No match for the identifier (in the requested market, if given), or no data in the window. | #N/A |
AMBIGUOUS_IDENTIFIER | Multiple candidates; qualify with a market suffix or use an entityId. | #SPILL! |
INVALID_IDENTIFIER | Empty or malformed identifier. | #VALUE! |
UNSUPPORTED_MARKET | Market code outside US · BR · MX · PE · CL · CO. | #N/A |
UNSUPPORTED_METRIC | Unknown metric, or a metric/period combination this endpoint doesn't serve. | #NAME? |
FX_UNAVAILABLE | Requested currency cannot be priced for some rows. | #N/A |
EXECUTION_ERROR | A batch job or handler failed; detail explains. | #VALUE! |
429 + Retry-After | Per-key rate limit exceeded (default 1,200 requests/minute; per-key overrides on creation, admin approval above the default). Honour Retry-After before retrying. | the add-in retries up to four times on its own; only then EDI rate limit reached |
Rendering rule (clients). Every row error carries a detail
string with instructions. In a context entirely about one security — a scalar
=EDI() cell or a single-security spill (HISTORY,
FUNDAMENTALS, RETURNS, CA) — clients render the symbol
plus the detail (e.g. #N/A — no match for ticker 'ZZNOSUCH' in market US),
so the fix reaches the sheet. Multi-security grid cells (MATRIX) keep the bare
symbol. These are strings, not native Excel error values, so ISNA/IFERROR
semantics are unaffected. Programmatic consumers should key on errors[].code,
never on cell text.
Request-level failures use standard HTTP statuses: 400 malformed request ·
401 unauthenticated · 404 unknown/expired batch id. The add-in
surfaces 4xx detail text in the cell as well.
Request limits
| Endpoint | Synchronous | Batch (batch:"Y") |
|---|---|---|
| /resolve | 2,000 identifiers | — |
| /prices — single day | 1,000 ids | 2,000 ids |
| /prices — multi-day | 100 ids | 1,000 ids |
| /fundamentals | 250 ids × 50 metrics | 5,000 ids × 50 metrics |
| /matrix | 500 ids × 50 metrics | 2,000 ids × 50 metrics |
| /screener/search | 1,000 rows per page | same, queued |
Clients & Excel
Python. The reference client (edi_analytics.py) wraps login, the
envelope, and batch polling, and ships the spill helpers used by the Excel integration —
spill_scalar, spill_series, spill_fundamentals,
spill_ratios, spill_matrix, spill_fibonacci — each returning a 2-D grid ready for a dataframe or a dynamic array.
Excel. The add-in registers formulas over exactly this API. Start with the Excel installation guide, which covers both Microsoft 365 administrator deployment and individual installation. You can also download the production manifest or the demo workbook directly.
=EDI.VALUE("AAPL-US", "MARKET_CAP") → one value =EDI.HISTORY("BBAS3-BR", "price", "2026-01-01", "2026-07-30", "M") → spills 7×2 =EDI.FUNDAMENTALS("AAPL-US", "REVENUE", "LTM") → spills fiscal periods =EDI.MATRIX(A2:A20, B1:F1) → spills a comps grid
Bad inputs degrade cell-by-cell (#N/A, #NAME?) — never a failed
workbook refresh. The add-in batches formula requests through batch
execution automatically.
Anything else. The OpenAPI 3 document drives generated clients for other stacks.
Try it
Sign in with your EDI account and run live requests against the API — every example on this page is runnable. Requests execute with your entitlements, exactly as they would from Excel or a script.
Staged capabilities
These request axes are part of the contract but deliberately rejected (HTTP 400, with an explanatory message) until their backing machinery ships — you can code against the vocabulary today without risk of silent wrong answers:
basis: "restated"— restated financials, pending vintage modeling.periodicity: "SEMI"— semi-annual reporters, pending normalization.adjust: "UNSPLIT"/"SPLIT_SPINOFF"— pending adjustment-factor history on the serving path. The factors themselves are available today on /corporate-actions and through bulk export.- Consensus estimates — no feed behind the dataset. Reported financials are the only source served.
- Funds and screener content domains.