ConcurredConcurred API

Chat API

Battle, Fight, and legacy chat endpoints

Use these endpoints when you want multiple AI models to answer the same question — side-by-side, or competing and voting on each other's answers. They can also search the web on their own when your question needs current information.

Endpoints

EndpointWhat it doesStreaming
POST /api/v1/battleTwo models answer the same question, side-by-side. No voting.Streaming or single response
POST /api/v1/fightUp to 8 models answer, see each other's answers, and vote on the best one.Streaming only
POST /api/v1/chat/completionsA single model answers. Works with the OpenAI SDK. See Gateway API.Streaming or single response
POST /api/chat (older)The original endpoint. Does everything above, but you have to pick the mode with a mode field in the request body. Still fully supported — see the dedicated Chat API (Legacy) page.Streaming or single response

Which one should I use?

  • Battle (2 models) → /api/v1/battle
  • Fight (3+ models with voting) → /api/v1/fight
  • Single model/api/v1/chat/completions (see the Gateway API for OpenAI-SDK-compatible usage)

The older /api/chat endpoint still works if you're already using it. New projects should use one of the three above.

Conversation History

All modes support multi-turn conversations via the optional messages array. Pass previous turns so the AI has context from earlier in the conversation.

{
  "message": "Can you explain that in simpler terms?",
  "mode": "chat",
  "model": "claude",
  "messages": [
    {"role": "user", "content": "What is quantum computing?"},
    {"role": "assistant", "content": "Quantum computing uses quantum-mechanical phenomena..."}
  ]
}

The messages array contains all previous turns before the current message. Each entry has:

FieldTypeDescription
rolestring"user", "assistant", or "system"
contentstringThe message content

How History Works Per Mode

  • Chat mode: The model sees the full conversation history, enabling natural follow-ups.
  • Battle mode: Both models see the same conversation history.
  • Fight mode (brawl, default): Every agent sees the conversation history, plus every peer answer from earlier in the round, plus every message from prior rounds.
  • Fight mode (blitz): Every agent sees the conversation history and only the winning answer from each prior round — never in-flight peers.
  • Multi-instance mode: Runs inside fight mode, so it follows whichever fightStyle you pass.

Web Search (Exa-Powered)

In battle, fight, and multi-instance modes, AI models can autonomously search the web when they need current information. This happens transparently — no extra parameters needed.

How It Works

  1. A model receives your question and decides it needs fresh data
  2. The model triggers a web search with a targeted query
  3. Results are fetched via Exa and fed back to the model
  4. The model's final response includes a citations array with sources

No Configuration Required

Web search is automatic. Models decide when to search based on the question. Not every response triggers a search.

Citations

When a model performs a web search, its response includes a citations array:

{
  "agent": "grok",
  "message": "As of February 2026, the S&P 500 is trading at...",
  "citations": [
    {
      "url": "https://example.com/article",
      "title": "Market Update: S&P 500 Hits New High",
      "snippet": "The S&P 500 reached a record close on Monday...",
      "publishedDate": "2026-02-09",
      "score": 0.95
    }
  ]
}

Streaming Search Events

When streaming, web searches produce additional SSE events:

data: {"type":"search_start","agent":"grok","query":"S&P 500 price February 2026","timestamp":1706540065}
data: {"type":"search_content","agent":"grok","delta":"The S&P 500 index...","timestamp":1706540066}
data: {"type":"search_complete","agent":"grok","citations":[...],"timestamp":1706540068}
ModeWeb Search
Chat (single model)No
Battle (2 models)Yes
Fight (multi-agent)Yes
Multi-instance (4x same model)Yes

Vision & Image Analysis

All modes support image inputs via the image_url parameter. Vision models (GPT, Claude, Gemini, Grok) see the image directly. Non-vision models receive an AI-generated description transparently. See Models > Vision Support for the full support matrix.

{
  "message": "What is in this image?",
  "mode": "chat",
  "model": "claude",
  "image_url": "https://example.com/photo.jpg"
}

In battle/fight mode, vision and non-vision models can compete on the same image — the system handles it automatically.


Chatting with a single model

If you just want one AI model to answer (no battle, no fight), use the Gateway API at POST /api/v1/chat/completions. It works with the OpenAI SDK — point the SDK at Concurred and your existing code runs unchanged. It also supports streaming, images, tool calls, and prompt caching.

import OpenAI from 'openai';
const client = new OpenAI({
  apiKey: process.env.CONCURRED_API_KEY,
  baseURL: 'https://concurred.ai/api/v1',
});
const completion = await client.chat.completions.create({
  model: 'claude',
  messages: [{ role: 'user', content: 'What is the best programming language?' }],
});

See the Gateway docs for the full reference.


Battle — POST /api/v1/battle

Compare responses from 2 AI models side by side.

Request

{
  "message": "Explain machine learning",
  "models": ["claude", "gpt"],
  "stream": false
}

You don't need a mode field — this endpoint is always battle mode. Everything else in the body works the same as the older /api/chat endpoint.

Response

{
  "id": "battle_1234567890_claude_gpt",
  "mode": "battle",
  "responses": [
    {"agent": "claude", "model": "claude", "message": "..."},
    {"agent": "gpt", "model": "gpt", "message": "...", "citations": [
      {"url": "https://...", "title": "...", "snippet": "..."}
    ]}
  ],
  "created_at": 1234567890
}

No Winner in Battle Mode

Battle mode returns raw responses only — no voting, no winner. Use fight mode if you need voting and a winner.


Fight — POST /api/v1/fight

Multiple AIs compete, debate, and vote on each other's responses.

Streaming Required

Fight requires streaming ("stream": true or omit the parameter). Sending "stream": false returns a 400 STREAMING_REQUIRED error.

Request

{
  "message": "What is the meaning of life?",
  "models": "claude,gpt,grok,gemini",
  "rounds": 2,
  "fightStyle": "brawl"
}

You don't need a mode field — this endpoint is always fight mode.

Selecting Models

Pass a comma-separated string ("claude,gpt,grok") or an array (["claude","gpt","grok"]). Omit models to use all 8 AI models.

Fight Styles

Pick how agents interact within a round with the optional fightStyle parameter.

fightStyleBehaviourFeels like
"brawl" (default)Agents answer sequentially within a round — each agent sees every peer answer that came before it. Multi-round rolls every prior round's full transcript forward.A live debate. Reactive, peer-aware, slower.
"blitz"Agents answer concurrently within a round from a frozen context — they never see in-flight peer answers. Multi-round carries only the previous round's winner forward.Parallel competition. Everyone strikes at once, faster, cleaner.

Omitting fightStyle is the same as "brawl", so existing integrations keep their current behaviour. Unknown values fall back to "brawl".

session_start echoes the chosen style so UIs can render either flow:

data: {"type":"session_start","mode":"fight","fightStyle":"blitz",...}

Response (SSE Stream)

Fight mode returns Server-Sent Events. Each line follows data: {json}\n\n:

data: {"type":"session_start","mode":"fight","agents":["claude","gpt","grok","gemini"],"rounds":2}
data: {"type":"round_start","round":1,"totalRounds":2}
data: {"type":"agent_start","agent":"claude","model":"claude"}
data: {"type":"agent_complete","agent":"claude","message":"..."}
data: {"type":"voting_start","round":1}
data: {"type":"vote_update","voter":"claude","votedFor":"gpt"}
data: {"type":"voting_complete","round":1,"results":[{"agent":"gpt","votes":42},...]}
data: {"type":"leaderboard_update","leaderboard":[{"agent":"gpt","score":42,"rank":1},...]}
data: {"type":"round_complete","round":1,"winner":"gpt"}
data: {"type":"session_complete","winner":"gpt","finalLeaderboard":[...]}

SSE Event Reference

EventKey FieldsDescription
session_startagents[], roundsSession begins
round_startround, totalRoundsNew round begins
agent_startagent, modelAgent about to respond
agent_completeagent, message, citations?Agent response ready
search_startagent, queryAgent triggered web search
search_completeagent, citations[]Search results ready
voting_startroundVoting phase begins
vote_updatevoter, votedForSingle vote cast
voting_completeround, results[]All votes tallied
leaderboard_updateleaderboard[]Cumulative scores
round_completeround, winnerRound winner
session_completewinner, finalLeaderboard[]Final results
errorerror, agent?Error occurred

Multi-Instance Mode

Use agentMode to run 4 instances of the same model competing. Similar to xAI's Grok Heavy.

agentModeDescription
multiple-grok4 Grok instances
multiple-claude4 Claude instances
multiple-gpt4 GPT instances
multiple-gemini4 Gemini instances
multiple-deepseek4 DeepSeek instances
multiple-kimi4 Kimi instances
multiple-mistral4 Mistral instances
multiple-llama4 Llama instances
{
  "message": "What's the best investment strategy?",
  "mode": "fight",
  "agentMode": "multiple-grok",
  "rounds": 2
}

Same SSE stream format as fight mode. Agent IDs will be grok-1, grok-2, grok-3, grok-4.


All Parameters

ParameterTypeRequiredDescription
messagestringYesYour question (max 2000 characters)
modestringYes"chat", "battle", or "fight"
modelstringFor chatSingle model ID (see Models)
modelsarray or stringFor battle/fightModel IDs. Array ["claude","gpt"] or comma-separated "claude,gpt"
image_urlstringNoPublic image URL for vision analysis
messagesarrayNoConversation history {role, content} objects
roundsnumberNoDebate rounds 1-10 (default: 1)
agentModestringNoMulti-instance mode (e.g., "multiple-grok")
fightStylestringNo"brawl" (default, sequential peer-aware) or "blitz" (parallel, frozen context). Fight mode only.
streambooleanNoEnable SSE streaming (default: true). Must be true for fight mode.

Mode Comparison

FeatureChatBattleFight
Models122-8
Web SearchNoYesYes
Voting & WinnerNoNoYes
stream: falseYesYesNo

SDK Examples

import requests
import json
 
response = requests.post(
    "https://concurred.ai/api/chat",
    headers={"X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json"},
    json={
        "message": "What's the best way to learn programming?",
        "mode": "fight",
        "models": "claude,gpt,grok,gemini",
        "rounds": 2
    },
    stream=True
)
 
for line in response.iter_lines():
    if line:
        text = line.decode('utf-8')
        if text.startswith('data: '):
            event = json.loads(text[6:])
            if event['type'] == 'agent_complete':
                print(f"{event['agent']}: {event['message'][:100]}...")
            elif event['type'] == 'session_complete':
                print(f"Winner: {event['winner']}")

Already using /api/chat?

The original POST /api/chat endpoint is still fully supported — it picks the mode from a mode field in the request body instead of from the URL. See the dedicated Chat API (Legacy) page for the mode mapping and a short migration checklist.