EDI Analytics API
v1
Quickstart OpenAPI

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.

Base URL https://api.edi.finance Markets US · BR · MX · PE Format JSON (camelCase) Auth session cookie OpenAPI 3 spec ↗

Quickstart

Authenticate once — the session cookie carries your entitlements on every subsequent call.

bash
# 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:

python
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"])

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.

json
{
  "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)

ValueMeaning
FYFiscal years, as reported.
QTRTrue fiscal quarters. 10-Qs stored as year-to-date are de-cumulated server-side; Q4 derives from the fiscal-year total when unreported.
LTMRolling sum of the last four quarters at each quarter-end (flows). Point-in-time items return their value as of that quarter-end.
YTDCumulative within each fiscal year.
SEMIStaged — awaiting semi-annual normalization.

basis selects the restatement axis: original (default) or restated (staged — awaits vintage modeling).

Frequency (prices & series)

ValueObservations kept
D / ADEvery observed trading day.
W · M · CQ · CSA · CYLast trading day of each ISO week / calendar month / quarter / half / year.
AM · AQ · ASA · AYAnchored: stepping from your startDate (a June 16 start yields Jun 16, Jul 16, …), taking the latest observation at or before each anchor.

Price adjustment

ValueMeaning
SPLITSplit-adjusted (default; matches stored venue closes).
DIV_SPIN_SPLITSDividend-, spinoff- and split-adjusted.
UNSPLIT · SPLIT_SPINOFFStaged — 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

POST/api/analytics/v1/resolve

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

json
// 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"} ] } ] }

Metric catalog

GET/api/analytics/v1/catalog/metrics

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

FilterMeaning
datasetfinancials_standardized · prices · valuation · derived · returns · entity_attributes · …
searchMatches code, name, and aliases (?search=revenue).
valueTypecurrency · percent · ratio · price · shares · text · …
entityTypecompany · etf · fund · bond · manager
includeDeprecatedDeprecated metrics carry a replacementMetricCode.
limit / offsetPagination; 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.

Global prices

POST/api/content/global-prices/v1/prices

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

json
// 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 },  ] }

Fundamentals

POST/api/content/fundamentals/v1/fundamentals

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

json
// 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 },  ] }

Matrix

POST/api/analytics/v1/matrix

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

json
// 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" } ] }

Screener

POST/api/content/screener/v1/search

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

json
// 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 } },  ] }

Funds

POST/api/content/funds/v1/…

Local-market fund data across Brazil (65,000+ CVM-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:

EndpointReturns
POST /summaryRegistry attributes: name, classification, operator/administrator, benchmark, status, native currency.
POST /pricesNAV series (fields: ["nav", "netAssets"]) with the full frequency vocabulary.
POST /returnsPeriodic returns computed from NAV at the sampled frequency. Fund NAVs are accumulation-style, so these are total returns.
POST /flowsDaily inflows / outflows / net flow — BR only (CVM daily reporting); other markets return a row error, never silence.
json
// POST /api/content/funds/v1/prices
{ "ids": ["00000684000121", "+TASAD1F1"], "fields": ["nav", "netAssets"],
  "startDate": "2026-01-01", "endDate": "2026-07-30", "frequency": "M", "currency": "USD" }

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.

StepCallResponse
1. SubmitPOST any data endpoint with "batch":"Y"202 + Location header; body carries the job id
2. PollGET /api/analytics/v1/batch-status?id=…202 while running · 201 when done (+Location) · 200 with status:"error" on failure
3. FetchGET /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.

Currency conversion

Conversion is server-side and semantics-aware — the right rate kind applies to each value:

Value kindRate applied
Price observationsSpot 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, textNever 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.

Error taxonomy

CodeMeaningExcel mapping
UNRESOLVED_IDENTIFIERNo match for the identifier (in the requested market, if given), or no data in the window.#N/A
AMBIGUOUS_IDENTIFIERMultiple candidates; qualify with a market suffix or use an entityId.#SPILL!
INVALID_IDENTIFIEREmpty or malformed identifier.#VALUE!
UNSUPPORTED_MARKETMarket code outside US · BR · MX · PE · CL · CO.#N/A
UNSUPPORTED_METRICUnknown metric, or a metric/period combination this endpoint doesn't serve.#NAME?
FX_UNAVAILABLERequested currency cannot be priced for some rows.#N/A
EXECUTION_ERRORA batch job or handler failed; detail explains.#VALUE!

Request-level failures use standard HTTP statuses: 400 malformed request · 401 unauthenticated · 404 unknown/expired batch id.

Request limits

EndpointSynchronousBatch (batch:"Y")
/resolve2,000 identifiers
/prices — single day1,000 ids2,000 ids
/prices — multi-day100 ids1,000 ids
/fundamentals250 ids × 50 metrics5,000 ids × 50 metrics
/matrix500 ids × 50 metrics2,000 ids × 50 metrics
/screener/search1,000 rows per pagesame, 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_matrix — each returning a 2-D grid ready for a dataframe or a dynamic array.

Excel. The add-in registers formulas over exactly this API:

excel
=EDI("AAPL-US", "MARKET_CAP")                      → 4,925,847,973,597
=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: