Programmatic access to Axion's forecasting platform. Send a question; Axion runs an orchestrator and a team of research agents (web search, market data, SEC filings, Fermi estimates) that reason in public, cite their evidence, and return graded probabilistic forecasts.
All endpoints require an API key passed as a Bearer token in the Authorization header:
Authorization: Bearer axn_sk_...
API keys are created and managed on the API Keys page. Any signed-in account can create a key. No paid plan required. Keys are shown once at creation and cannot be retrieved later.
https://axion-main-api-axion---productionus.us-east-1.gists.org
API usage is billed separately from the Axion monthly plan. Users pre-purchase credits before usage.
| Model | Input rate | Output rate | Cached rate |
|---|---|---|---|
| Opus 4.8 | 750 | 3,750 | 75 |
| Sonnet 4.6 | 450 | 2,250 | 45 |
Rates are per 1M tokens. A single thread may use multiple models (coordinator + agents), so costs accumulate across all calls. Negative balances are allowed to guarantee delivery of in-progress results.
This reference is built to drop straight into a coding agent (Claude Code, Cursor, and similar). At the top of this page, use Copy as Markdown to copy the full reference formatted for LLMs, or Copy as Text for a plain-text version. Paste it into your agent's context and it has everything it needs to write a correct integration: authentication, request and response shapes, polling, and error handling.
A prompt that works well:
Here are the Axion API docs. Write a <language> client that creates a forecast, polls until it completes, and prints each forecast with its probability. Read the API key from an AXION_API_KEY environment variable.
/forecastsStart a new forecast thread, or send a follow-up message to an existing one. Requires a positive API credit balance.
| Field | Type | Required | Description |
|---|---|---|---|
| input | string | required | The question or analysis prompt. |
| id | string | optional | Existing thread ID for follow-up messages. Not allowed with mode=decision. |
| mode | string | optional | Run type. Omit (default) for a forecast; decision runs one-shot Decision Mode (admin only). See Decision Mode. |
| max_forecasts | integer | optional | Number of forecasts to generate (1-10). Default 1. Not allowed with mode=decision. |
| effort | string | optional | Analysis depth: low, medium, high, or xhigh. Default medium. |
| tier | string | optional | Cost tier: default or scout. Scout runs a multi-model fusion panel — lower cost, slower — billed at fusion cost. Requires the costTiers entitlement on your account (403 otherwise). Frozen at thread creation: on follow-up (id set) omit it to keep the thread's tier; a conflicting value is rejected. Not allowed with mode: "decision". |
| monitoring | object | optional | Monitoring cadence for the live decision. Only valid with mode: "decision". See Decision Mode. |
| webhook_url | string | optional | URL to receive a POST callback on completion or failure. |
{
"input": "Will NVIDIA's data center revenue exceed $40B in Q1 2026?",
"max_forecasts": 3,
"effort": "high"
}{
"id": "thread_abc123",
"input": "Will NVIDIA's data center revenue exceed $40B in Q1 2026?",
"preliminary_result": null,
"status": "starting"
}/forecasts/{thread_id}Poll the status and results of a forecast thread.
startingin_progresscompletedfailed{
"id": "thread_abc123",
"input": "Will NVIDIA's data center revenue exceed $40B in Q1 2026?",
"preliminary_result": "Early read: data-center momentum and hyperscaler capex point above $40B, but HBM supply is the swing factor. Refining with filings and channel checks…",
"status": "completed",
"result": "NVIDIA's data-center segment has grown for six straight quarters, reaching $35.6B in Q4 FY25. The Blackwell ramp and committed hyperscaler capex support a Q1 2026 figure above $40B; the main downside is HBM3E supply. On balance the threshold is more likely than not to be cleared…",
"credits_consumed": 4231,
"agents_progress": [
{ "slug": "web-search", "progress": 1.0, "turn": 12, "action": "done" },
{ "slug": "sec-filings", "progress": 1.0, "turn": 8, "action": "done" }
],
"forecasts": [
{
"forecast_text": "NVIDIA data center revenue will exceed $40B in Q1 2026",
"probability": 0.72,
"confidence_lower": 0.55,
"confidence_upper": 0.85,
"resolution_date": "2026-07-15",
"reasoning": "Q4 FY25 data-center revenue was $35.6B (+16% QoQ). Blackwell shipments and hyperscaler capex guidance imply continued double-digit sequential growth, placing $40B within reach absent a supply shock…",
"concludes_at": "2026-07-15",
"is_concluded": false,
"outcome": null,
"outcome_reasoning": null,
"created_at": "2026-04-14T10:30:00Z"
}
],
"artifacts": [
{ "component_name": "RevenueChart", "props": "{\"data\": [...]}" }
]
}/forecastsList all forecast threads for your account.
{
"forecasts": [
{
"id": "thread_abc123",
"input": "Will NVIDIA's data center revenue...",
"status": "completed",
"credits_consumed": 4231,
"created_at": "2026-04-14T10:30:00Z"
}
]
}/forecasts/{thread_id}/stopCancel an in-progress forecast. Credits consumed up to cancellation are still charged.
{ "success": true }/forecasts/{thread_id}Delete a forecast thread.
{ "success": true }/account/balanceReturns your current credit balance.
{ "credits": 3750 }/account/credits/purchaseCreate a Stripe Checkout session for a one-time credit purchase. Minimum amount is $50.
{ "amount": 50 }{ "checkout_url": "https://checkout.stripe.com/c/pay/cs_..." }Decision Mode turns one description of a real decision into a live decision model — the alternatives, the value function that scores them, and the forecasts that drive the outcome — then keeps it current as new evidence arrives. In the app it runs as a conversation. Over the API it runs in one shot: submit the full description once. There are no follow-up questions; the run infers any missing details (a name, a deadline, the status-quo option) from your input and states the assumptions it made in the summary.
Decision Mode is restricted to admins. A non-admin key receives 403 on both create and poll.
Submit with mode: "decision". The request takes input, webhook_url, and effort like a forecast; id and max_forecasts do not apply. One call does everything: the run assembles the model, propagates it into the live decision store, and arms continuous monitoring. There is no second step.
{
"input": "We can extend our Series A runway by cutting the new EU expansion, or raise a bridge now at a lower valuation. Decide by end of Q3. We care most about 18-month survival, then growth.",
"mode": "decision",
"effort": "high",
"monitoring": { "cron": "0 6 * * *", "timezone": "America/New_York" }
}A live decision is monitored daily by default: the backend re-reads evidence on a schedule and folds it into the decision nodes, so each poll reflects the current recommendation. Override the cadence with a monitoring object. It is only valid with mode: "decision".
| Field | Type | Required | Description |
|---|---|---|---|
| enabled | boolean | optional | Whether to monitor the decision. Default true. |
| cron | string | optional | Schedule in cron syntax. Default 0 6 * * * (daily at 06:00). |
| timezone | string | optional | IANA timezone the schedule runs in. Default America/New_York. |
Poll GET /forecasts/{thread_id}. For a decision run the signal to watch is the decision envelope's state, not the top-level status: the decision is usable the moment state reaches live, which can happen before — and independently of — the thread's status settling to completed. Poll until state is live (or the run reports failed). The decision field is an envelope with a state:
none — the run has not produced a decision model yet, or it completed without one.draft — the model is assembled and about to go live. Transient.live — the model is propagated and monitored. The fields below are populated.result holds the written summary once the run produces one; it may still be empty while the decision is already live, so don't gate on it. A live decision is folded fresh on every poll, so computed reflects the latest evidence and updated_at advances as monitoring moves the nodes.
{
"id": "thread_abc123",
"status": "completed",
"result": "Two alternatives modeled against 18-month survival and growth. Cutting EU expansion preserves runway with lower upside; the bridge raise funds growth at dilution cost…",
"decision": {
"state": "live",
"decision_id": "dec_abc123",
"updated_at": "2026-06-29T06:00:00Z",
"monitoring": { "enabled": true, "cron": "0 6 * * *", "timezone": "America/New_York" },
"computed": {
"recommendation": {
"top_id": "alt_cut",
"runner_up_id": "alt_bridge",
"margin_pp": 12.4,
"flipped_from_baseline": false
},
"gate": "release",
"launch_p": 0.62,
"change_type": "no_material_change",
"confidence": 0.71,
"confidence_delta_pp": 3.2,
"scored": [
{
"id": "alt_cut", "label": "Cut EU expansion", "score": 0.62, "allocation": 1.0,
"history": [
{ "date": "2026-06-20", "allocation": 0.8 },
{ "date": "2026-06-29", "allocation": 1.0 }
],
"changes": [
{
"date": "2026-06-29", "from_allocation": 0.8, "to_allocation": 1.0,
"trigger_forecast_id": "fc_survive", "evidence_ids": ["ev_1"],
"change_type": "budget_shift"
}
]
},
{ "id": "alt_bridge", "label": "Raise a bridge round", "score": 0.5, "allocation": 0.0, "history": [], "changes": [] }
],
"forecasts": [
{
"id": "fc_survive", "label": "Company survives the next 18 months",
"current_probability": 0.78, "baseline_probability": 0.72, "delta_pp": 6.0,
"initial_forecast_source": "axion_api",
"history": [
{ "date": "2026-06-20", "p": 0.72, "evidence_ids": [] },
{ "date": "2026-06-29", "p": 0.78, "evidence_ids": ["ev_1"] }
]
}
]
},
"events": [
{ "id": "ev_1", "day": 1, "date": "2026-06-29", "kind": "evidence",
"setForecast": { "id": "fc_survive", "p": 0.78 } }
],
"pack": { "pack_version": "1", "decision_nodes": [] }
}
}computed is at full parity with the web app — everything the UI renders, the API returns: recommendation (top alternative, runner-up, margin, and flipped_from_baseline); the confidence gate (release, review, defer, or hold) with its launch_p; the decision-level change_type (no_material_change, ranking_change, budget_shift, gate_state_change, or confidence_change); confidence and its signed move (confidence_delta_pp); every scored alternative with its allocation history and per-option changes; and each driving forecast's current and baseline probability, delta_pp, initial_forecast_source (llm_prior or axion_api), history trajectory, and belief state. On a just-minted decision the deltas are 0 and per-option changes are empty; each alternative's history carries a single initial allocation point, while forecast trajectories stay empty until the first monitoring cycle records evidence. events is the raw evidence-ingest log behind every change — omitted until the first monitoring cycle records evidence; once present it is an array of ingest events (each a kind, date, and the forecast or weight move it applied). pack carries the full decision model and is abridged above. Decision Mode charges credits the same way a forecast does.
If webhook_url is provided when creating a forecast, Axion sends a POST request to that URL when the thread reaches a terminal state (completed or failed). The payload matches the GET /forecasts/{thread_id} response.
Delivery is best-effort with up to 3 retries. Poll as a fallback if you need guaranteed delivery.
All errors return a JSON body with an error field:
{ "error": "insufficient credits" }| Status | Meaning |
|---|---|
| 400 | Invalid request |
| 401 | Invalid or missing API key |
| 402 | Insufficient credits |
| 403 | Restricted to admins (Decision Mode) |
| 404 | Thread not found |
| 422 | Invalid request body |
| 429 | Too many concurrent threads (max 10) |
| 500 | Internal error |
import requests
import time
API_KEY = "axn_sk_..."
BASE = "https://axion-main-api-axion---productionus.us-east-1.gists.org"
headers = {"Authorization": f"Bearer {API_KEY}"}
# Create
r = requests.post(f"{BASE}/forecasts", headers=headers, json={
"input": "Will the Fed cut rates in June 2026?",
"effort": "high"
})
thread_id = r.json()["id"]
# Poll
while True:
r = requests.get(f"{BASE}/forecasts/{thread_id}", headers=headers)
data = r.json()
print(f"Status: {data['status']}")
if data["status"] in ("completed", "failed"):
break
time.sleep(5)
# Result
for f in data["forecasts"]:
print(f"{f['forecast_text']}: {f['probability']}")Eternis Inc.