API Reference
Integrate StockDebate AI into your agents and applications. Authenticate with an API key or pay per-call with USDC via x402.
Overview
StockDebate AI provides two independent integration paths. Choose the one that fits your use case.
sdai_... key. Credits are deducted per debate. Supports all modes including interactive debates and real-time SSE streaming.Base URL
https://api.stockdebate.ai/v1
API Key
Include your key as a Bearer token on every request. Generate keys on the API Keys page.
Authorization: Bearer sdai_YOUR_KEY
Supports all debate modes: automatic and interactive. Credits are deducted from the pack associated with the key.
Create Debate
/debatesEnqueues a new debate and deducts one credit. Returns a debate_id immediately — generation happens asynchronously. Stream it in real-time or poll for results.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
| ticker | string | Yes | Stock ticker (e.g. NVDA, 7203, 00700) |
| market | string | Yes | Exchange code. See Enums. |
| language | string | No | Debate language. Default: "en". See Enums. |
| mode | string | No | "automatic" (default) or "interactive" |
| format | string | No | "text" (default) or "vocal" |
| length | string | No | "short" (default) or "long" |
Response — 201 Created
{ "debate_id": "550e8400-e29b-41d4-a716-446655440000" }Error responses
| Field | Type | Required | Description |
|---|---|---|---|
| 402 | status | No | No credits remaining — purchase a credit pack first |
| 422 | status | No | Ticker not found on the specified market |
curl -X POST https://api.stockdebate.ai/v1/debates \
-H "Authorization: Bearer sdai_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"ticker":"NVDA","market":"us","format":"text","length":"short"}'Get Debate
/debates/:idReturns the full debate including all generated rounds. Poll until status === "done", or use the SSE stream for real-time updates.
Response fields
| Field | Type | Required | Description |
|---|---|---|---|
| id | string | No | Debate UUID |
| ticker | string | No | Stock ticker |
| market | string | No | Exchange code |
| status | string | No | "queued" | "generating" | "done" | "failed" |
| format | string | No | "text" | "vocal" |
| mode | string | No | "automatic" | "interactive" |
| rounds | array | No | Array of debate rounds |
| created_at | string | No | ISO 8601 timestamp |
Round object
| Field | Type | Required | Description |
|---|---|---|---|
| round_number | number | No | 1-based round index |
| moderator_text | string? | No | Moderator's opening and framing |
| pitcher_text | string? | No | Bull case argument |
| doubter_text | string? | No | Bear case argument |
| proposal_text | string? | No | Next-round topic options (interactive) or conclusion prompt |
| end_of_debate | boolean | No | True on the final concluding round |
| selected_option | string? | No | Interactive only — option chosen: "1" | "2" | "3" | "4". null otherwise. |
| custom_topic | string? | No | Interactive only — custom topic text when selected_option is "4". null otherwise. |
curl https://api.stockdebate.ai/v1/debates/550e8400-e29b-41d4-a716-446655440000 \ -H "Authorization: Bearer sdai_YOUR_KEY"
Stream Debate (SSE)
/debates/:id/streamServer-Sent Events stream delivering rounds in real-time as they are generated. Use the Last-Event-ID header to reconnect and replay missed events from a specific round.
Event types
| Field | Type | Required | Description |
|---|---|---|---|
| round_ready | event | No | A new debate round is available. Payload contains all speaker texts. |
| audio_ready | event | No | TTS audio for a single speaker is ready (vocal debates only). |
| audio_merged | event | No | Full debate audio is merged and ready for download (vocal debates only). |
| done | event | No | Debate generation is complete. Includes total_rounds. |
| error | event | No | Generation failed. Includes error message. |
Example — JavaScript
const es = new EventSource(
`https://api.stockdebate.ai/v1/debates/${debateId}/stream`,
{ headers: { Authorization: "Bearer sdai_YOUR_KEY" } }
);
es.addEventListener("round_ready", (e) => {
const round = JSON.parse(e.data);
console.log(`Round ${round.round_number}:`, round.moderator_text);
});
es.addEventListener("done", () => {
console.log("Debate complete");
es.close();
});Advance Interactive Debate
/debates/:id/advanceSubmits the user's topic selection for the current round of an interactive debate. The debate must have mode: "interactive" and status: "generating". The worker pauses after each round waiting for this call.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
| option | string | Yes | "1" deepen current topic | "2" next topic | "3" conclude | "4" custom topic |
| custom_topic | string? | No | Required when option is "4". The custom topic to inject into the next round (max 200 chars). |
Response — 200 OK
{ "ok": true }Example — move to next topic
curl -X POST https://api.stockdebate.ai/v1/debates/DEBATE_ID/advance \
-H "Authorization: Bearer sdai_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"option":"2"}'Example — custom topic
curl -X POST https://api.stockdebate.ai/v1/debates/DEBATE_ID/advance \
-H "Authorization: Bearer sdai_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"option":"4","custom_topic":"Discuss the impact of US tariffs on supply chain"}'Convert to Audio
/debates/:id/convert-to-audioTriggers post-hoc TTS synthesis for a completed text debate. Deducts one vocal credit. Returns immediately — listen for the audio_merged SSE event, or poll GET /debates/:id until format === "vocal".
curl -X POST https://api.stockdebate.ai/v1/debates/DEBATE_ID/convert-to-audio \ -H "Authorization: Bearer sdai_YOUR_KEY"
Get Audio
/debates/:id/audio/mergedReturns the merged MP3 of the full vocal debate. Available after the audio_merged SSE event fires.
curl -o debate.mp3 \ https://api.stockdebate.ai/v1/debates/DEBATE_ID/audio/merged \ -H "Authorization: Bearer sdai_YOUR_KEY"
/debates/:id/audio/:round/:roleReturns the MP3 for a single speaker segment. Available after the audio_ready SSE event for that segment. role is one of moderator, pitcher, doubter, proposal.
Full Examples
Python — create, stream, and print
import requests, sseclient, json
API_KEY = "sdai_YOUR_KEY"
BASE = "https://api.stockdebate.ai/v1"
HEADERS = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}
# 1. Create debate
r = requests.post(f"{BASE}/debates", headers=HEADERS, json={
"ticker": "NVDA", "market": "us", "language": "en",
"format": "text", "length": "short"
})
r.raise_for_status()
debate_id = r.json()["debate_id"]
print(f"Created: {debate_id}")
# 2. Stream rounds in real-time (pip install sseclient-py)
stream = requests.get(
f"{BASE}/debates/{debate_id}/stream",
headers={**HEADERS, "Accept": "text/event-stream"},
stream=True
)
client = sseclient.SSEClient(stream)
for event in client.events():
if event.event == "round_ready":
rnd = json.loads(event.data)
print(f"\n── Round {rnd['round_number']} ──")
print(f"Moderator : {rnd['moderator_text'][:200]}...")
print(f"Bull : {rnd['pitcher_text'][:200]}...")
print(f"Bear : {rnd['doubter_text'][:200]}...")
elif event.event == "done":
print("\nDebate complete.")
breakTypeScript — create and stream live
// npm install @microsoft/fetch-event-source
import { fetchEventSource } from "@microsoft/fetch-event-source";
const API_KEY = "sdai_YOUR_KEY";
const BASE = "https://api.stockdebate.ai/v1";
const headers = { Authorization: `Bearer ${API_KEY}`, "Content-Type": "application/json" };
// 1. Create debate
const { debate_id } = await fetch(`${BASE}/debates`, {
method: "POST",
headers,
body: JSON.stringify({ ticker: "AAPL", market: "us", format: "text", length: "short" }),
}).then(r => r.json());
// 2. Stream in real-time
await fetchEventSource(`${BASE}/debates/${debate_id}/stream`, {
headers,
onmessage(ev) {
if (ev.event === "round_ready") {
const round = JSON.parse(ev.data);
console.log(`Round ${round.round_number}:`, round.moderator_text);
}
if (ev.event === "done") console.log("Debate complete!");
},
});x402 (Agents / Pay-per-call)
No account or sign-up needed. Pay per call in USDC on Base. Designed for autonomous agents.
How It Works
x402 is a payment protocol where each HTTP request is independently priced. Your USDC stays in your wallet until the moment of the call — there is no deposit or running balance.
Pricing
| Format | Price | USDC amount (6 decimals) |
|---|---|---|
| text | $0.50 | 500000 |
| vocal | $3.00 | 3000000 |
Payment flow
- Send the request without auth → server returns
402with a payment descriptor. - Sign an EIP-3009
transferWithAuthorizationagainst the descriptor using an x402 client library. - Resend the identical request with the signed payload in the
X-Paymentheader. - The server verifies via Coinbase facilitator, settles on-chain, and processes the debate.
Payment descriptor (402 body)
{
"x402Version": 1,
"accepts": [{
"scheme": "exact",
"network": "eip155:8453",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"maxAmountRequired": "500000",
"resource": "https://api.stockdebate.ai/v1/a2a/debates",
"description": "StockDebate AI — Text Debate ($0.50 USDC)",
"mimeType": "application/json",
"payTo": "0x<server-wallet>",
"maxTimeoutSeconds": 300,
"extra": { "name": "USDC", "version": "2" }
}]
}Agent discovery
Agents can pre-discover payment capabilities without making a live request:
curl https://api.stockdebate.ai/.well-known/payment-manifest.json
Create Debate
/a2a/debatesEnqueues a new debate. Only mode: automatic is supported via x402 — interactive debates are available via the web UI only.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
| ticker | string | Yes | Stock ticker symbol (e.g. NVDA, 7203, 00700) |
| market | string | Yes | Exchange code. See Enums. |
| language | string | No | Debate language. Default: "en". See Enums. |
| format | string | No | "text" (default) or "vocal" |
| length | string | No | "short" (default) or "long" |
Response — 201 Created
{ "debate_id": "550e8400-e29b-41d4-a716-446655440000" }Get Debate
/a2a/debates/:idReturns the full debate including all generated rounds. Poll until status === "done". The round schema is identical to the API Key endpoint, with selected_option and custom_topic always null (automatic mode only).
Bearer sdai_...) or an X-Payment header from a prior x402 call. When using the x402 Python/TS client, auth is carried through automatically — no extra header needed.# With API key
curl https://api.stockdebate.ai/v1/a2a/debates/DEBATE_ID \
-H "Authorization: Bearer sdai_YOUR_KEY"
# With x402 client (Python) — auth handled automatically
debate = client.get(f"https://api.stockdebate.ai/v1/a2a/debates/{debate_id}").json()Get Audio
For vocal debates (format: "vocal"), retrieve the generated MP3 once the debate is done. Audio endpoints are public — no auth required, only the debate ID.
/debates/:id/audio/mergedReturns the full merged MP3 of the debate. Available once status === "done" on a vocal debate.
curl -o debate.mp3 \ https://api.stockdebate.ai/v1/debates/DEBATE_ID/audio/merged
/debates/:id/audio/:round/:roleReturns the MP3 for a single speaker segment. role is one of moderator, pitcher, doubter, proposal.
curl -o round1_bull.mp3 \ https://api.stockdebate.ai/v1/debates/DEBATE_ID/audio/1/pitcher
Agent Flow Example
End-to-end example for an autonomous agent using the x402 Python client. The agent pays per call with no prior setup.
# pip install x402
from x402.client import X402Client
import time
client = X402Client(wallet_private_key="0xYOUR_PRIVATE_KEY")
BASE = "https://api.stockdebate.ai/v1"
# 1. Create debate (x402 handles the 402 challenge and payment automatically)
response = client.post(
f"{BASE}/a2a/debates",
json={"ticker": "NVDA", "market": "us", "format": "text", "length": "short"}
)
response.raise_for_status()
debate_id = response.json()["debate_id"]
print(f"Debate created: {debate_id}")
# 2. Poll until done
while True:
r = client.get(f"{BASE}/a2a/debates/{debate_id}")
debate = r.json()
if debate["status"] == "done":
break
if debate["status"] == "failed":
raise RuntimeError("Generation failed")
print(f"Status: {debate['status']} ({len(debate.get('rounds', []))} rounds so far)")
time.sleep(5)
# 3. Print rounds
for rnd in debate["rounds"]:
print(f"\n── Round {rnd['round_number']} ──")
print(f"Moderator : {rnd['moderator_text'][:200]}...")
print(f"Bull : {rnd['pitcher_text'][:200]}...")
print(f"Bear : {rnd['doubter_text'][:200]}...")Reference
Enums, options, and error codes shared across all endpoints.
Enums & Options
market| "us" | US (NYSE / NASDAQ) |
| "hk" | HK (HKEX) |
| "cn" | CN (SSE / SZSE) |
| "jp" | JP (Tokyo Stock Exchange) |
| "kr" | KR (Korea Exchange) |
language| "en" | English (default) |
| "zh" | Chinese (Mandarin) |
| "ja" | Japanese |
| "ko" | Korean |
format| "text" | Text-only debate (default) |
| "vocal" | AI-voiced MP3 debate |
length| "short" | ~3 rounds (default) |
| "long" | ~5 rounds |
Error Codes
| Status | Meaning |
|---|---|
| 200 | OK — request successful |
| 201 | Created — debate enqueued |
| 202 | Accepted — operation queued |
| 204 | No Content — deleted / revoked |
| 400 | Bad Request — malformed JSON or missing required field |
| 401 | Unauthorized — missing or invalid API key |
| 402 | Payment Required — insufficient credits or x402 payment needed |
| 404 | Not Found — debate or resource does not exist |
| 409 | Conflict — duplicate operation (e.g. audio conversion already running) |
| 422 | Unprocessable Entity — ticker not found on the specified market |
| 500 | Internal Server Error — generation failed; credit will be refunded |
Error responses include a JSON body: { "error": "message" }