Sign up and get 3 free requests with Start plan accessSign up →

API

Programmatic access to the same indices the site publishes: the catalog, the LLM Market Outlook and its history, the daily calibration rollup, and the smart-consensus council with its weights and accuracy series. Every endpoint below is deployed and public.

The live indices · How the numbers are built · Plans and pricing

Base URL /api/v1JSONISO-8601 UTC timestampscamelCase fieldsGET only

Every endpoint sits under one versioned base URL and returns JSON. Response fields are camelCase throughout — expectedMove, not expected_move — including inside nested objects. Query parameters stay literal and lower-case: symbol, fh, days, slug.

GET https://marketmania.ai/api/v1/indices/outlook/?symbol=BTC&fh=4h
→ 200 application/json

Every timestamp inside a payload — generatedAt, ts, latestTs — is ISO-8601 in UTC, for example 2026-08-04T09:01:00+00:00. Dates written for people, in this reference and across the site, are rendered en-GB instead, for example 4 Aug 2026: UTC for machines, en-GB for readers.

Horizons are lower-case on the wire. The monthly horizon is 1m in every request and response. It is displayed as 1M everywhere a human reads it, so a client that echoes the raw value into a UI will show "1 minute" where the API means one month.

The indices are public by design. There is no key, no header and no account needed to read any endpoint on this page: they are anonymous, identical-for-everyone reads of a table a scheduled job has already materialised. A 401 from one of these paths would be a bug, not a state to handle.

Responses are page-cached for 60 seconds, so polling faster than that returns the same bytes. Requests share the public benchmarks rate limit; exceeding it returns 429. The indices are recomputed on a schedule, not per request — the freshest data any caller can get is the latest slot, which each payload states in generatedAt and latestTs.

Plans and pricing are not on this page. Nothing on /api/v1/indices/ is metered today. What the account plans cover is on Prices, and that is the only place a price is stated.

PathParametersReturns
GET /indices/slug?Catalog: which indices exist, the live symbol and horizon vocabularies, and how fresh each index is.
GET /indices/outlook/symbol? fh?Latest row per horizon for symbol, the 30-day scorecard, and every instrument's latest row at fh.
GET /indices/outlook/history/symbol? fh? days?Slot-granular ts, sentiment, label series. days defaults to 30 and is clamped to 365.
GET /indices/calibration/symbol?The latest daily calibration row for the scope. No horizon parameter — one row spans all five.
GET /indices/smart-consensus/symbol? fh?Latest council call, the weights behind it, the 30-day daily accuracy series and the scorecard.

symbol is one of ALL, BTC, ETH, SOL, BNB, XRP and defaults to ALL. fh is one of 1h, 4h, 1d, 1w, 1m and defaults to 4h. Both vocabularies are also published by the catalog endpoint, which is the safe thing to build a client against.

Catalog

GET /indices/ · what exists and how fresh it is
{
  "generatedAt": "2026-08-04T10:09:42.165465+00:00",
  "variant": "v0",
  "symbols": ["ALL", "BTC", "ETH", "SOL", "BNB", "XRP"],
  "horizons": ["1h", "4h", "1d", "1w", "1m"],
  "indices": [
    { "slug": "outlook",     "description": "…", "latestTs": "2026-08-04T09:01:00+00:00" },
    { "slug": "calibration", "description": "…", "latestTs": "2026-08-03T00:00:00+00:00" },
    { "slug": "smart",       "description": "…", "latestTs": "2026-08-04T09:01:00+00:00" },
    { "slug": "smart_daily", "description": "…", "latestTs": "2026-08-03T00:00:00+00:00" }
  ]
}

A latestTs of null means the index has published no row at all yet. The two daily slugs carry a UTC midnight, not a live slot: they are rollups of a closed day, so the newest complete row is normally yesterday's.

Outlook

GET /indices/outlook/ · directional reading for one scope
GET /api/v1/indices/outlook/?symbol=ALL&fh=4h

{
  "generatedAt": "2026-08-04T10:09:39.929001+00:00",
  "symbol": "ALL",
  "fh": "4h",
  "horizons": [ { …row per horizon… } ],
  "scorecard": [
    { "fh": "1h", "n": 465, "hitPct": 49.2473, "strongN": 358, "strongHitPct": 49.7207 }
  ],
  "acrossInstruments": [ { …row per instrument at fh… } ]
}

Arrays above are abridged. horizons holds one row per horizon for symbol; acrossInstruments holds one row per instrument at fh, ALL first. Each item carries its own ts, because a quiet coin's latest row can be older than its neighbours'.

FieldTypeMeaning
fhstringForecast horizon, one of 1h / 4h / 1d / 1w / 1m.
symbolstringInstrument, or ALL for the pooled reading.
tsstringSlot instant, ISO-8601 UTC.
sentimentnumber | nullWeighted directional score, −100 … +100.
labelstringBullish / Bearish / Sideways at the ±20 band.
consensusnumber | nullWeight on the winning side, 0–100.
sharesobject | nullWeighted share per side: bull, sideways, bear.
expectedMovenumber | nullWeighted median take-profit distance over the directional forecasts, in percent.
convictionnumber | nullPer-model z-scored confidence as 50 + 25·mean(z). An index score centred on 50, NOT a percentage — with z clipped to ±3 the range is [−25, 125].
disagreementnumber | nullNormalised entropy of the three shares, 0–100.
nForecastsnumberForecasts behind the row.
nModelsnumberDistinct models behind the row.
resolvedbooleanTrue once the horizon has closed.
realizedMovePctnumber | nullRealised move over the horizon, in percent.
realizedDirnumber | null+1 / −1 / 0 once resolved, else null.

Outlook history

GET /indices/outlook/history/ · slot-granular series
GET /api/v1/indices/outlook/history/?symbol=BTC&fh=4h&days=30

{ "symbol": "BTC", "fh": "4h", "days": 30, "n": 178,
  "points": [ { "ts": "…", "sentiment": -3.3869, "label": "Sideways" } ] }
Gaps stay gaps. A missing slot is never back-filled or interpolated, so a client charting this series must plot points and must not assume a fixed cadence between them.

Calibration

GET /indices/calibration/ · daily rollup, one scope
GET /api/v1/indices/calibration/?symbol=ALL

{ "symbol": "ALL", "ts": "2026-08-03T00:00:00+00:00", "date": "2026-08-03",
  "gap": -16.8013, "stated": 63.8365, "actual": 47.0352, "n": 10962,
  "buckets": [ { "bucket": "50-60", "lo": 50, "hi": 60, "n": 2262,
                 "stated": 56.9089, "actual": 47.7454, "gap": -9.1636 } ],
  "byModel":   [ { "model": "claude-fable-5", "n": 1467, "stated": 60.4901,
                   "actual": 46.9666, "gap": -13.5235, "discrimination": -2.045 } ],
  "byHorizon": [ { "fh": "1h", "n": 6682, "stated": 63.7206,
                   "actual": 48.683, "gap": -15.0376 } ],
  "below50":   { "n": 0, "stated": null, "actual": null } }
200 with empty fields, never 404. A scope with no row yet answers 200 with ts, date and gap as null, n as 0, and empty arrays. Do not render a null gap as 0.0 — zero would publish "perfectly calibrated", which is a claim the row is not making.
FieldTypeMeaning
symbolstringScope of the row: ALL or one instrument.
tsstring | nullRow slot — a UTC midnight. Null when no row exists yet.
datestring | nullThe closed UTC day the window ends on, YYYY-MM-DD.
gapnumber | nulln-weighted actual minus stated, in percentage points. Null, never 0, when the window holds no bucketed call.
statednumber | nulln-weighted mean stated confidence.
actualnumber | nulln-weighted mean realised accuracy.
nnumberBucketed population: settled directional calls stated at 50 or above.
buckets[]arrayFive 10-point bands: bucket, lo, hi, n, stated, actual, gap.
byModel[]arraymodel, n, stated, actual, gap, discrimination (null below 30 window rows).
byHorizon[]arrayfh, n, stated, actual, gap. Absent horizons are omitted.
below50objectSub-50-confidence calls: n, stated, actual. Excluded from buckets.

Smart consensus

GET /indices/smart-consensus/ · council call, weights, accuracy
GET /api/v1/indices/smart-consensus/?symbol=ALL&fh=4h

{ "generatedAt": "…", "symbol": "ALL", "fh": "4h",
  "latest": { "ts": "2026-08-04T08:01:00+00:00", "score": -2.8571, "label": "Bearish",
              "majorityScore": -2.8571, "nForecasts": 70, "nModels": 7,
              "resolved": false, "realizedMovePct": null, "realizedDir": null,
              "weights": [ { "model": "claude-fable-5", "weight": 0.1429 } ] },
  "weights":  [ { "model": "claude-fable-5", "weight": 0.1429 } ],
  "accuracy": [ { "date": "2026-07-17", "ts": "…", "smartHitPct": 50.0,
                  "majorityHitPct": 50.0,
                  "bestModel": { "model": "qwen-3.7-max", "hitPct": 63.8298, "n": 47 },
                  "n": 12 } ],
  "scorecard": [ { "fh": "4h", "n": 115, "hitPct": 47.8261,
                   "strongN": 91, "strongHitPct": 46.1538 } ] }
FieldTypeMeaning
latestobject | nullThe most recent council call for the scope, or null.
latest.scorenumber | nullWeighted council score, −100 … +100.
latest.labelstringSide of the council call. This is sign(score) — there is NO ±20 dead band on this index.
latest.majorityScorenumber | nullThe same vote, unweighted.
weights[]arraymodel and weight (0–1, summing to 1) behind the current call.
accuracy[]arrayDaily rollups: date, ts, smartHitPct, majorityHitPct, bestModel, n. A day appears once its forecasts have resolved.
scorecard[]arrayfh, n, hitPct, strongN, strongHitPct over the resolved council calls of the trailing 30 days.
Two label rules, on purpose. The council side is sign(score) with no dead band; the outlook label uses the ±20 Sideways band. The same instrument and horizon can therefore carry Sideways from /outlook/ and Bullish from /smart-consensus/. See Implementation notes.

StatusWhen
200Success — including the "no row for this scope yet" case, which is an empty payload rather than an error.
400An unknown symbol, fh or slug. Window sizes are clamped rather than rejected, so days never 400s.
429Public rate limit exceeded. Back off and retry.
GET /api/v1/indices/outlook/?fh=2h
→ 400

{ "errors": { "fh": ["unknown horizon '2h', must be one of ['1h', '4h', '1d', '1w', '1m']"] },
  "error": "fh: unknown horizon '2h', must be one of ['1h', '4h', '1d', '1w', '1m']" }

Both keys are always emitted: errors is the field-keyed shape the rest of the site reads, error is the flat one-liner. Neither consumer needs a special case.

The following are planned and are listed here so that their absence is explicit rather than implied. None of them exists on /api/v1/indices/ today, and nothing above depends on them.

PlannedStatus
API keys and per-key quotasIn development. The indices are open and unauthenticated in the meantime.
CSV responses (?format=csv)In development. Every endpoint above is JSON only today.
Webhooks on index updatesIn development. Poll the catalog's latestTs for freshness.
Embeddable widgetsIn development.
Bulk dataset exportsIn development — see Data.
Analyze API (programmatic AI market reports)In development.
MCP server for assistantsIn development — see Plugins.