Appearance
Prediction Markets Reports API
Retrieve Octagon's research reports for prediction markets directly over REST — pull an event's report and its recent versions, get its Trust Index scorecard, or trigger fresh report generation and check its status, without going through the Prediction Markets Agent.
The report body is the same markdown research memo the agent produces.
Field style, timestamps, and probability scales follow the Conventions. Base URL:
text
https://api.octagonai.co/v1Endpoints
Each marketplace has its own endpoints, addressed by that marketplace's native event key.
| Method | Path | Description | Credits |
|---|---|---|---|
GET | /predictions/reports/kalshi/{event_ticker} | Report versions for a Kalshi event | Free |
GET | /predictions/reports/polymarket/{event_slug} | Report versions for a Polymarket event | Free |
GET | /predictions/reports/kalshi/{event_ticker}/trust | Trust Index scorecard for a Kalshi event | Free |
GET | /predictions/reports/polymarket/{event_slug}/trust | Trust Index scorecard for a Polymarket event | Free |
POST | /predictions/reports/kalshi/{event_ticker} | Generate a fresh Kalshi report (asynchronous) | 3 |
POST | /predictions/reports/polymarket/{event_slug} | Generate a fresh Polymarket report (asynchronous) | 3 |
GET | /predictions/reports/status/{run_id} | Status of a generation run (no report body) | Free |
Credits per call are listed above. See the Prediction Markets Agent for plan eligibility.
Event Keys
| Endpoints | Key | Example |
|---|---|---|
/reports/kalshi/… | Event ticker | KXBTC-25DEC31 |
/reports/polymarket/… | Event slug | fed-decision-in-september |
Address markets by event key. The Prediction Markets Agent also accepts market URLs.
Retrieve Report Versions
text
GET /predictions/reports/kalshi/{event_ticker}
GET /predictions/reports/polymarket/{event_slug}Returns one version of the event's report — the latest by default — with the event's recent versions, newest first. Each version is one analysis run.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
version | string | No | latest (the default) for the newest version, or a run_id to pin that version. Any run_id the event produced resolves, including one older than the listed versions. |
Response Fields
| Field | Type | Description |
|---|---|---|
event_ticker | string | The resolved event key. |
venue | string | kalshi or polymarket, matching the endpoint called. |
requested_url | string | null | The resolved market URL on the marketplace. |
name | string | null | Human-readable event name, as of the returned version. |
title | string | null | Same as name. |
versions | array | Recent versions, newest first (up to 6). Empty when nothing has been generated yet. |
run_id | string | null | The returned version: the latest, or the one version pins. null when nothing has been generated. |
markdown_report | string | null | That version's full report as markdown. |
trust_score | float | null | That version's Trust Index score (0–100). The full scorecard is at Get the Trust Index. |
trust_label | string | null | That version's Trust Index label: Strong, Good, Caution, High Risk, or Avoid. |
outcome_probabilities_json | string | null | That version's per-market probability breakdown, as a JSON string. Items carry model_probability_source and evidence_grade — see Interpreting the model probability. |
Each entry in versions is that run's headline:
| Field | Type | Description |
|---|---|---|
run_id | string (UUID) | The run that produced this version. Pass it back as ?version=. |
captured_at | string | When the snapshot was captured. |
analysis_last_updated | string | When the underlying analysis was last refreshed. |
market_probability | float | Market-implied probability. |
model_probability | float | Octagon model probability. |
confidence_score | float | Octagon's confidence in the model output. |
total_volume | float | Total trading volume at capture time. |
key_takeaway | string | One-line summary of the analysis. |
trust_score | float | null | Trust Index score for this version (0–100). |
trust_label | string | null | Trust Index label for this version. |
A version is a completed analysis, so versions[0] is always the latest report.
Status Codes
| Status | Meaning |
|---|---|
200 | The event key resolved. versions may be empty when nothing has been generated for it yet. |
400 | Malformed event key, or an unknown query parameter (invalid_value). |
404 | The event key matches nothing, or ?version=<run_id> is not a version of the event (not_found). |
Get the Trust Index
text
GET /predictions/reports/kalshi/{event_ticker}/trust
GET /predictions/reports/polymarket/{event_slug}/trustReturns the Trust Index scorecard for one report version. The Trust Index rates how far a market's price can be relied on, as an overall score blended from two axes: Integrity (market integrity, information fairness, resolution quality) and Trade quality (liquidity, move quality, rule clarity). The report response carries the headline as trust_score and trust_label.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
version | string | No | latest (the default) or a run_id. Names the same version as on Retrieve Report Versions. |
expand | string | No | Comma-separated axes to return in depth: integrity, trade_quality. See Expanded detail. |
Response Fields
| Field | Type | Description |
|---|---|---|
event_ticker | string | The resolved event key. |
venue | string | kalshi or polymarket, matching the endpoint called. |
title | string | null | Human-readable event name, as of the scored version. |
run_id | string (UUID) | The version the scorecard belongs to. |
trust_index | object | The scorecard. |
trust_index:
| Field | Type | Description |
|---|---|---|
score | integer | Overall score, 0–100. |
label | string | Strong, Good, Caution, High Risk, or Avoid. |
computed_at | string | When the scorecard was computed. The latest version's scorecard is refreshed hourly. |
version | string | Calculation version. |
caps | array | Caps that lowered the score, in the order applied: key, name, floor, ceiling. The last one set the score. |
uncapped_score | integer | null | The blended score before caps, when a cap lowered it. |
floors_breached | array | Pillars under their safety floor whose cap changed nothing, because the score was already below its ceiling: key, floor, score. |
profile | object | The two axes, integrity and trade_quality. |
profile.integrity:
| Field | Type | Description |
|---|---|---|
key, name | string, string | Always integrity and Integrity. |
score, label | integer, string | The axis score and its label. Both null when the axis has no reading. |
risk | object | null | Integrity risk exposure: score (0–100, higher is more exposed) and label. |
breakdown | array | The three pillars, always in this order: manipulation_resistance (Market integrity), information_fairness (Info fairness), settlement_reliability (Resolution quality). Each has key, name, score, label, basis, summary, and factors. |
screen_counts | object | null | Integrity screens by state: run, not_applicable, awaiting_data. |
A pillar's basis is measured when screens applied, or structural_prior when none did and the score comes from the market's structure.
profile.trade_quality:
| Field | Type | Description |
|---|---|---|
key, name | string, string | Always trade_quality and Trade quality. |
score, label | integer, string | The axis score and its label. Both null when the axis has no reading. |
basis, summary, factors | string, string, array | How the score was reached, as on the integrity pillars. factors includes the cost of a $1,000 order. |
breakdown | array | The three components, always in this order: liquidity, move_quality, rule_clarity. Each has key, name, score, and label. |
Every pillar and component is always listed. One without a reading has score and label set to null. Match caps and breached floors to pillars by key.
json
{
"event_ticker": "KXPRESPERSON-28",
"venue": "kalshi",
"title": "2028 U.S. Presidential Election winner?",
"run_id": "6f1d2c3a-8b4e-4f0a-9c7d-2e5b1a0f3c9d",
"trust_index": {
"score": 40, "label": "High Risk",
"computed_at": "2026-09-22T12:50:39Z", "version": "underwriting_v1.4",
"caps": [{ "key": "manipulation_resistance", "name": "Market integrity", "floor": 30, "ceiling": 40 }],
"uncapped_score": 48,
"floors_breached": [],
"profile": {
"integrity": {
"key": "integrity", "name": "Integrity", "score": 46, "label": "High Risk",
"risk": { "score": 82, "label": "High exposure" },
"breakdown": [
{ "key": "manipulation_resistance", "name": "Market integrity", "score": 20, "label": "Avoid",
"basis": "measured", "summary": "…", "factors": ["…"] },
{ "key": "information_fairness", "name": "Info fairness", "score": 40, "label": "High Risk", "…": "…" },
{ "key": "settlement_reliability", "name": "Resolution quality", "score": 92, "label": "Strong", "…": "…" }
],
"screen_counts": { "run": 6, "not_applicable": 1, "awaiting_data": 3 },
"screens": null, "structure_classes": null
},
"trade_quality": {
"key": "trade_quality", "name": "Trade quality", "score": 60, "label": "Caution",
"basis": "measured", "summary": "…", "factors": ["$1,000 order: ~2.1% cost", "…"],
"breakdown": [
{ "key": "liquidity", "name": "Liquidity", "score": 80, "label": "Deep" },
{ "key": "move_quality", "name": "Move quality", "score": 65, "label": "Mostly confirmed" },
{ "key": "rule_clarity", "name": "Rule clarity", "score": 100, "label": "Clear" }
],
"markets": null, "exclusions": null
}
}
}
}Expanded detail
expand deepens an axis; both axes are always present. A detail that wasn't expanded is null.
expand | Adds |
|---|---|
integrity | profile.integrity.screens: one entry per integrity screen, with key, state (run, not_applicable, or awaiting_data), score, label, confidence, drivers, evidence (text, window), and warning. Also profile.integrity.structure_classes. |
trade_quality | profile.trade_quality.markets: one card per scored market, with market_ticker, title, is_primary, last_trade_cents, and scores for market_quality, liquidity, move_quality, and resolution_clarity. Each score has score, label, confidence, not_applicable, drivers, evidence, and warning. Also profile.trade_quality.exclusions: markets left out of the read, as total, terminal_lifecycle, below_volume_floor, and no_ticker. |
Status Codes
| Status | Code | Meaning |
|---|---|---|
200 | The scorecard for the requested version. | |
400 | invalid_value | Malformed event key, an unknown expand value, or an unknown query parameter. |
404 | report_not_found | The event key matches nothing, or no report has been generated yet. |
404 | version_not_found | ?version=<run_id> is not a version of the event. |
404 | trust_index_not_found | The version exists but has no Trust Index. |
Generate a Fresh Report
text
POST /predictions/reports/kalshi/{event_ticker}
POST /predictions/reports/polymarket/{event_slug}Triggers fresh report generation for a market. There is no request body — the market is identified entirely by the path. Generation is asynchronous. The request returns 202 Accepted immediately with a run_id.
Response Fields (202 Accepted)
| Field | Type | Description |
|---|---|---|
run_id | string (UUID) | The generation run. Poll GET /predictions/reports/status/{run_id} with it. |
status | string | Always "processing" on acceptance. |
event_ticker | string | The resolved event key. |
venue | string | kalshi or polymarket, matching the endpoint called. |
The 3-credit charge is applied when the 202 is returned. If generation subsequently fails, the credits are automatically refunded.
Requirements
- Market status: the market must be open on the marketplace.
Errors
| Status | Code | Meaning |
|---|---|---|
400 | invalid_value | Malformed event key. |
400 | kalshi_url_required | No event matches the key. |
400 | kalshi_reference_ambiguous | The key matches multiple events. |
401 | no_subscription | No active subscription on the API key. |
403 | insufficient_plan | Fresh generation requires a Plus, Pro, or Enterprise plan. |
404 | kalshi_market_not_found | The market could not be found on the marketplace. |
409 | kalshi_market_not_open | The market is not open on the marketplace (expired). |
429 | insufficient_credits | Not enough credits for the 3-credit charge. |
503 | service_unavailable | The generation service could not be reached. |
Errors use the envelope {"error": {"message", "type", "param", "code"}}.
Check a Run's Status
GET /predictions/reports/status/{run_id}
Returns the status of a generation run. Poll it after triggering fresh generation. When status is "completed", fetch the markdown from the marketplace endpoint using the returned venue and event_ticker, with ?version=latest or ?version=<run_id>.
| Parameter | Type | Required | Description |
|---|---|---|---|
run_id | string (UUID) | Yes | The run ID returned by a POST to /reports. |
Response Fields
| Field | Type | Description |
|---|---|---|
run_id | string (UUID) | The requested run. |
status | string | "processing", "completed", or "failed". |
venue | string | kalshi or polymarket — which marketplace endpoint to fetch from. |
event_ticker | string | null | The resolved event key for the run. |
requested_url | string | null | The resolved market URL for the run. |
A known run always returns 200 — poll until status leaves "processing". A "failed" run means generation did not complete. 404 (not_found) means the run_id is unknown.
TIP
If you only need the report body as soon as it's ready, you can skip this endpoint and poll the marketplace endpoint with ?version=latest directly — markdown_report stays null until the new version lands.
Notes
- Poll every 30–60 seconds; fresh generation typically takes several minutes.
- The
run_idreturned by aPOSTis the single join key: use it to check run status, to pin a version on retrieval (?version=<run_id>), and to reconcile the credit charge. - The report markdown includes a
Trust Index: <score> · <label>line. The full scorecard is at Get the Trust Index.