Read your first forecast
Find a forecast, read its latest run and retrieve the reasoning behind it with a short Python script.
In the API, a forecast is a question and each run is a forecast object; see Forecasts in the API.
The snippets come from quickstart.py.
1. Set up Python and credentials
Use Python 3.11 or newer. Ask your Mantic contact or support@mantic.com for a client ID and client secret.
In Bash on macOS or Linux, create an environment and install httpx:
python3 -m venv .venv
source .venv/bin/activate
python -m pip install httpx==0.28.1Set your client ID and client secret in your terminal, replacing the placeholders:
export MANTIC_CLIENT_ID="YOUR_CLIENT_ID"
export MANTIC_CLIENT_SECRET="YOUR_CLIENT_SECRET"2. Exchange credentials for an access token
Request an access token from the authentication endpoint:
def get_access_token(http: httpx.Client) -> str:
response = http.post(
"https://app.mantic.com/auth/oauth2/token",
auth=(os.environ["MANTIC_CLIENT_ID"], os.environ["MANTIC_CLIENT_SECRET"]),
data={"grant_type": "client_credentials", "resource": "https://api.mantic.com"},
)
response.raise_for_status()
return response.json()["access_token"]resource must be exactly https://api.mantic.com, with no trailing slash. The token expires after expires_in seconds; Get and refresh an access token shows how to renew it.
3. Find a forecast
Search by topic and print each returned forecast's id (its question_id) and title:
def search_questions(http: httpx.Client, search: str) -> list[dict[str, Any]]:
response = http.get("/questions", params={"search": search})
response.raise_for_status()
questions = response.json()["questions"]
for question in questions:
print(question["id"], question["title"])
return questionsSearch ranks forecasts by meaning and returns up to 25 results in a single page. The output looks like this:
123 Will UK CPI inflation be below 2.5% in December 2026?4. Read the selected forecast
Retrieve the forecast you chose with GET /questions/{question_id}. The response includes its latest run as latest_forecast:
def read_question(http: httpx.Client, question_id: int) -> dict[str, Any] | None:
response = http.get(f"/questions/{question_id}", params={"show_forecasts": "true"})
if response.status_code == 404:
return None # Not found, or no completed run yet
response.raise_for_status()
return response.json()A 404 means the forecast doesn't exist or has no completed run yet.
5. Interpret the latest run
Read the run's values according to its forecast type:
def show_forecast(forecast: dict[str, Any]) -> None:
match forecast["type"]:
case "event":
print(f"Probability: {forecast['probability']:.0%} by {forecast['by']}")
case "numerical" | "date":
median = forecast["percentiles"].get("50")
print(f"Median: {median if median is not None else 'unavailable'}")
case "categorical":
for category in forecast["categories"]:
print(f"{category['label']}: {category['probability']:.0%}")
case _:
print("Forecast values unavailable.")
print(f"Run ID: {forecast['id']} · Updated: {forecast['date']}")- For an event,
probabilityis a fraction:0.64means 64%.byis the event deadline.dateis when the run completed, not the deadline. - For numerical and date forecasts,
percentiles["50"]is the median; it can be missing. - Categorical forecasts give a probability for each labelled outcome.
- An
unknownrun has no values, so the script skips its reasoning. Withoutshow_forecasts=true, runs can come backredacted, with no values, so the script sends it.
For an event forecast, the output looks like this:
Probability: 64% by 2026-12-31T23:59:59Z
Run ID: 987 · Updated: 2026-09-21T08:30:00ZSee Read a forecast's latest run for each forecast type and its fields.
6. Retrieve the reasoning for the same run
Pass the run's id to GET /forecasts/{forecast_id} to retrieve the detailed run, including its reasoning:
def show_reasoning(http: httpx.Client, forecast_id: int) -> None:
response = http.get(f"/forecasts/{forecast_id}")
response.raise_for_status()
print(response.json().get("reasoning") or "No reasoning is available for this run.")reasoning is Markdown: the same reasoning you see on the forecast in the Mantic web app.
7. Put it together
run() requests the access token, searches, asks for a forecast's question_id and prints its latest run and reasoning:
def run(http: httpx.Client, search: str) -> None:
http.headers["Authorization"] = f"Bearer {get_access_token(http)}"
questions = search_questions(http, search)
if not questions:
print("No matching forecasts. Try a different search.")
return
choice = input("Enter a question_id from these results: ").strip()
if choice not in {str(question["id"]) for question in questions}:
print("Choose an ID from the search results above.")
return
question = read_question(http, int(choice))
if question is None or question["latest_forecast"] is None:
print("This forecast has no completed run yet. Choose another forecast.")
return
print(question["title"])
forecast = question["latest_forecast"]
show_forecast(forecast)
if forecast["type"] not in {"redacted", "unknown"}:
show_reasoning(http, forecast["id"])The script runs it with an httpx client pointed at the Mantic API:
with httpx.Client(base_url="https://api.mantic.com", timeout=30.0) as http:
run(http, args.search)Run the complete example
In the environment from step 1, run:
python quickstart.py --search "UK inflation"Enter a forecast's question_id when prompted to see its latest run and reasoning.
Examples on other pages
- cURL examples send an access token saved in
$ACCESS_TOKEN; Get and refresh an access token shows how to set it. - Python examples import
ManticClientfrommantic_client.py, which gets and refreshes the token for you. Keep it beside the script you run. - Download all examples (.tar.gz) for every script on this site, with setup instructions.