ManticDocs
Read forecasts

Read a forecast's latest run

Read a forecast's latest run by type, and the fields that define how the forecast resolves.

Fetch a forecast with GET /questions/{question_id}:

curl "https://api.mantic.com/questions/QUESTION_ID?show_forecasts=true" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

The latest run is latest_forecast; its fields depend on its type.

When there is no run yet

GET /questions/{question_id} returns 404 until the forecast has a completed run, and GET /questions leaves it out. GET /dashboards/{dashboard_id}/questions includes it with latest_forecast: null.

What the forecast says

These fields define what is being forecast and stay the same across runs:

FieldContains
titleThe forecast's title, phrased as a question.
declarative_titleThe title as a statement, or null.
clarificationsThe criteria that determine how the forecast resolves, or null.
supplementary_informationBackground and context, or null.
resolutionnull until the forecast resolves; then resolved_at, value (a string or null) and notes.
tagsTag names, or null when the forecast has none.

Event

Each run of an event forecast gives the probability that the event happens by by:

{
  "id": 987,
  "date": "2026-09-21T08:30:00Z",
  "type": "event",
  "probability": 0.64,
  "by": "2026-12-31T23:59:59Z"
}

probability is a fraction, so 0.64 is 64%. date is when this run completed; by is the outcome deadline.

Numerical

A numerical run gives percentiles: percentiles["25"] is the value the outcome has a 25% chance of being at or below.

{
  "id": 988,
  "date": "2026-09-21T08:30:00Z",
  "type": "numerical",
  "percentiles": {
    "25": 3.2,
    "50": 3.8,
    "75": 4.5
  },
  "thresholds": null,
  "intervals": null
}

The median is percentiles["50"]. Ignore the deprecated median_value field and p25/p75 keys; skip keys starting with p when you iterate. thresholds and intervals are currently always null; to get the probability above or below a value, see Get the probability above or below a value.

Date

A date run has the same shape, with timestamps as values:

{
  "id": 989,
  "date": "2026-09-21T08:30:00Z",
  "type": "date",
  "percentiles": {
    "25": "2026-11-04T00:00:00Z",
    "50": "2026-12-12T00:00:00Z",
    "75": "2027-02-03T00:00:00Z"
  },
  "thresholds": null,
  "intervals": null
}

The median is percentiles["50"]; median_date is deprecated. A date distribution may not reach every percentile, so don't assume 25, 50 and 75 are all present.

Categorical

Each run of a categorical forecast gives one probability per labelled outcome:

{
  "id": 990,
  "date": "2026-09-21T08:30:00Z",
  "type": "categorical",
  "categories": [
    { "label": "Hold", "probability": 0.55 },
    { "label": "Cut", "probability": 0.35 },
    { "label": "Raise", "probability": 0.10 }
  ]
}

Match categories by label, not by their position in the array.

Redacted and unknown

{
  "id": 991,
  "date": "2026-09-21T08:30:00Z",
  "type": "redacted"
}

redacted means the run exists but its values were left out: the request set show_forecasts=false, or omitted it and your organisation's default is off. Send show_forecasts=true to every endpoint that accepts it.

unknown is a run the public API can't represent. Treat it, and any type your code doesn't handle, like redacted: rely only on id and date.

Handle every forecast type

This function turns any latest_forecast, including null, into a one-line headline:

from typing import Any


def headline(forecast: dict[str, Any] | None) -> str:
    if forecast is None:
        return "No completed run yet"
    match forecast["type"]:
        case "event":
            return f"{forecast['probability']:.0%}"
        case "numerical" | "date":
            return str(forecast["percentiles"].get("50", "Median unavailable"))
        case "categorical":
            leader = max(forecast["categories"], key=lambda item: item["probability"])
            return f"{leader['label']}: {leader['probability']:.0%}"
        case "redacted" | "unknown":
            return "Forecast values unavailable"
        case other:
            return f"Unsupported forecast type: {other}"

On this page