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.

API Key
For developers building apps or scripts. Authenticate with a sdai_... key. Credits are deducted per debate. Supports all modes including interactive debates and real-time SSE streaming.
Jump to API Key docs →
x402 (agents / pay-per-call)
For autonomous agents. No account or sign-up required. Pay per call in USDC on Base using the x402 protocol. Automatic mode only.
Jump to x402 docs →

Base URL

url
https://api.stockdebate.ai/v1
Integration · API Key

API Key

Include your key as a Bearer token on every request. Generate keys on the API Keys page.

Authorization header
http
Authorization: Bearer sdai_YOUR_KEY

Supports all debate modes: automatic and interactive. Credits are deducted from the pack associated with the key.

Create Debate

POST/debates

Enqueues 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

FieldTypeRequiredDescription
tickerstringYesStock ticker (e.g. NVDA, 7203, 00700)
marketstringYesExchange code. See Enums.
languagestringNoDebate language. Default: "en". See Enums.
modestringNo"automatic" (default) or "interactive"
formatstringNo"text" (default) or "vocal"
lengthstringNo"short" (default) or "long"

Response — 201 Created

json
{ "debate_id": "550e8400-e29b-41d4-a716-446655440000" }

Error responses

FieldTypeRequiredDescription
402statusNoNo credits remaining — purchase a credit pack first
422statusNoTicker not found on the specified market
bash
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

GET/debates/:id

Returns the full debate including all generated rounds. Poll until status === "done", or use the SSE stream for real-time updates.

Response fields

FieldTypeRequiredDescription
idstringNoDebate UUID
tickerstringNoStock ticker
marketstringNoExchange code
statusstringNo"queued" | "generating" | "done" | "failed"
formatstringNo"text" | "vocal"
modestringNo"automatic" | "interactive"
roundsarrayNoArray of debate rounds
created_atstringNoISO 8601 timestamp

Round object

FieldTypeRequiredDescription
round_numbernumberNo1-based round index
moderator_textstring?NoModerator's opening and framing
pitcher_textstring?NoBull case argument
doubter_textstring?NoBear case argument
proposal_textstring?NoNext-round topic options (interactive) or conclusion prompt
end_of_debatebooleanNoTrue on the final concluding round
selected_optionstring?NoInteractive only — option chosen: "1" | "2" | "3" | "4". null otherwise.
custom_topicstring?NoInteractive only — custom topic text when selected_option is "4". null otherwise.
bash
curl https://api.stockdebate.ai/v1/debates/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer sdai_YOUR_KEY"

Stream Debate (SSE)

GET/debates/:id/stream

Server-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

FieldTypeRequiredDescription
round_readyeventNoA new debate round is available. Payload contains all speaker texts.
audio_readyeventNoTTS audio for a single speaker is ready (vocal debates only).
audio_mergedeventNoFull debate audio is merged and ready for download (vocal debates only).
doneeventNoDebate generation is complete. Includes total_rounds.
erroreventNoGeneration failed. Includes error message.

Example — JavaScript

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

POST/debates/:id/advance

Submits 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

FieldTypeRequiredDescription
optionstringYes"1" deepen current topic | "2" next topic | "3" conclude | "4" custom topic
custom_topicstring?NoRequired when option is "4". The custom topic to inject into the next round (max 200 chars).

Response — 200 OK

json
{ "ok": true }

Example — move to next topic

bash
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

bash
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

POST/debates/:id/convert-to-audio

Triggers 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".

bash
curl -X POST https://api.stockdebate.ai/v1/debates/DEBATE_ID/convert-to-audio \
  -H "Authorization: Bearer sdai_YOUR_KEY"

Get Audio

GET/debates/:id/audio/merged

Returns the merged MP3 of the full vocal debate. Available after the audio_merged SSE event fires.

bash
curl -o debate.mp3 \
  https://api.stockdebate.ai/v1/debates/DEBATE_ID/audio/merged \
  -H "Authorization: Bearer sdai_YOUR_KEY"
GET/debates/:id/audio/:round/:role

Returns 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

python
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.")
        break

TypeScript — create and stream live

typescript
// 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!");
  },
});
Integration · x402

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

FormatPriceUSDC amount (6 decimals)
text$0.50500000
vocal$3.003000000

Payment flow

  1. Send the request without auth → server returns 402 with a payment descriptor.
  2. Sign an EIP-3009 transferWithAuthorization against the descriptor using an x402 client library.
  3. Resend the identical request with the signed payload in the X-Payment header.
  4. The server verifies via Coinbase facilitator, settles on-chain, and processes the debate.

Payment descriptor (402 body)

json
{
  "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:

bash
curl https://api.stockdebate.ai/.well-known/payment-manifest.json

Create Debate

POST/a2a/debates

Enqueues a new debate. Only mode: automatic is supported via x402 — interactive debates are available via the web UI only.

Request body

FieldTypeRequiredDescription
tickerstringYesStock ticker symbol (e.g. NVDA, 7203, 00700)
marketstringYesExchange code. See Enums.
languagestringNoDebate language. Default: "en". See Enums.
formatstringNo"text" (default) or "vocal"
lengthstringNo"short" (default) or "long"

Response — 201 Created

json
{ "debate_id": "550e8400-e29b-41d4-a716-446655440000" }

Get Debate

GET/a2a/debates/:id

Returns 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).

Auth: Pass a valid API key (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.
bash
# 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.

GET/debates/:id/audio/merged

Returns the full merged MP3 of the debate. Available once status === "done" on a vocal debate.

bash
curl -o debate.mp3 \
  https://api.stockdebate.ai/v1/debates/DEBATE_ID/audio/merged
GET/debates/:id/audio/:round/:role

Returns the MP3 for a single speaker segment. role is one of moderator, pitcher, doubter, proposal.

bash
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.

python
# 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

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

StatusMeaning
200OK — request successful
201Created — debate enqueued
202Accepted — operation queued
204No Content — deleted / revoked
400Bad Request — malformed JSON or missing required field
401Unauthorized — missing or invalid API key
402Payment Required — insufficient credits or x402 payment needed
404Not Found — debate or resource does not exist
409Conflict — duplicate operation (e.g. audio conversion already running)
422Unprocessable Entity — ticker not found on the specified market
500Internal Server Error — generation failed; credit will be refunded

Error responses include a JSON body: { "error": "message" }

StockDebate AI