Developer API

Programmatic access to ModelChorus leaderboard data, model rankings, battle results, and votes. Build research tools, dashboards, and integrations on top of community-driven AI evaluations.

Authentication

Most read endpoints (leaderboard, models, votes, stats, suggestions) require no authentication. Polling endpoints (GET /api/v1/battles/:id and GET /api/v1/chats/:id) require a standard-tier key. Write endpoints (creating battles, voting, sending messages) require a write-tier key.

TierKey prefixCapabilities
AnonymousNoneLeaderboard, models, votes, stats, suggestions
Standardmc_All anonymous endpoints + poll battle and chat status
Writemc_All standard endpoints + create battles, vote, send chat messages

Self-service keys are standard tier (read + poll). Write-tier access (create battles, vote, send messages) is granted by MeetKai admins — contact us to request it.

API keys are intended for server-to-server use only. Do not embed them in client-side code or browsers — the CORS policy allows all origins, so a leaked key can be used by anyone.

Authorization header

http
Authorization: Bearer mc_0123456789abcdef0123456789abcdef

Base URL

All endpoints are served from the Convex deployment URL. Append the route path directly — no additional prefix is needed.

bash
# Base URL
https://http-actions.modelchorus.com

# Full endpoint example
GET https://http-actions.modelchorus.com/api/v1/leaderboard

The base URL is your *.convex.site host. Set NEXT_PUBLIC_CONVEX_SITE_URL explicitly, or it is derived automatically from NEXT_PUBLIC_CONVEX_URL.

Response Format & Errors

All responses are JSON. Success responses wrap the payload in a data field. Errors use a consistent envelope.

json
// Success
{ "data": { ... } }

// Paginated success
{ "data": [...], "pagination": { "hasMore": true, "nextCursor": "eyJ..." } }

// Error
{ "error": { "code": "RATE_LIMITED", "message": "Daily quota exceeded." } }
Error codeHTTPMeaning
UNAUTHORIZED401Missing or invalid API key
FORBIDDEN403Key lacks the required tier for this endpoint
NOT_FOUND404The requested resource does not exist
BAD_REQUEST400Malformed request body or query parameters
QUOTA_EXCEEDED429Daily battle quota exceeded for this key
RATE_LIMITED429Too many requests — slow down
TOO_EARLY429Vote submitted before responses finished streaming
VOTE_FAILED400Vote rejected — already voted, wrong state, or battle not ready
NOT_SUPPORTED400Operation not supported (e.g. multi-turn continuation on image battles)
INTERNAL_ERROR500Unexpected server error

Rate Limits

  • Write-tier keys: 100 battles per 24 hours (default). Quota uses a rolling 24-hour window from the first battle of each period — not a fixed midnight UTC reset.
  • Read endpoints: 120 req/min per IP for anonymous callers, per key for standard keys; 600 req/min per key for partner (read/write) keys. Server-side cache applies per the Cache column on each endpoint.
  • Maximum prompt/message length: 10,000 characters.
  • The limit query parameter on list endpoints accepts 1–50 (default 20).

Quickstart

Run your first battle in under a minute. Steps 1 and 3 require a write-tier API key. Step 2 requires a standard-tier key.

  1. 1. Create a battle — POST your prompt. The response includes an id and pollIntervalMs.
  2. 2. Poll until ready — GET the battle on the interval until responsesReady is true. The server also enforces a 30-second minimum from creation before a vote is accepted.
  3. 3. Vote — POST your winner choice. The response reveals both model identities in modelsRevealed.modelA and modelsRevealed.modelB.
BASE_URL="https://http-actions.modelchorus.com"
API_KEY="mc_your_key_here"

# 1. Create a battle (write-tier key required)
RESPONSE=$(curl -s -X POST "$BASE_URL/api/v1/battles" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"prompt": "Explain quantum entanglement simply.", "modality": "text"}')
BATTLE_ID=$(echo "$RESPONSE" | jq -r '.data.id')
START=$(date +%s)

# 2. Poll until both responses are ready
while true; do
  BATTLE=$(curl -s "$BASE_URL/api/v1/battles/$BATTLE_ID" \
    -H "Authorization: Bearer $API_KEY")
  READY=$(echo "$BATTLE" | jq -r '.data.responsesReady')
  [ "$READY" = "true" ] && break
  sleep 2
done

# Server enforces a 30s minimum from creation before voting
ELAPSED=$(( $(date +%s) - START ))
[ "$ELAPSED" -lt 30 ] && sleep $((30 - ELAPSED))

# 3. Vote (write-tier key required)
curl -s -X POST "$BASE_URL/api/v1/battles/$BATTLE_ID/vote" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"winner": "A"}' | jq '.data.modelsRevealed'
# { "modelA": "GPT-4o", "modelB": "Claude 3.5 Sonnet" }

Leaderboard

GET/api/v1/leaderboardNo auth requiredCache: 60s

Returns ranked model standings for text battles, ordered by Elo score descending.

ParameterTypeRequiredDescription
localestringnoFilter by locale code (e.g. en, es, fr). Omit for global rankings.
categorystringnoFilter by category slug. Requires locale to be set.

Example request

bash
curl https://http-actions.modelchorus.com/api/v1/leaderboard

Example response

json
{
  "data": [
    {
      "_id": "jd7abc123xyz",
      "_creationTime": 1714000000000,
      "modelName": "GPT-4o",
      "eloRating": 1247,
      "rank": 1,
      "wins": 831,
      "losses": 412,
      "ties": 94,
      "totalVotes": 1337,
      "winRate": 0.669,
      "inputCostPer1M": 5.0,
      "outputCostPer1M": 15.0,
      "valueScore": 249.4
    }
  ]
}
GET/api/v1/leaderboard/imageNo auth requiredCache: 60s

Returns ranked model standings for image generation battles, ordered by Elo score descending.

ParameterTypeRequiredDescription
localestringnoFilter by locale code.

Example request

bash
curl https://http-actions.modelchorus.com/api/v1/leaderboard/image
GET/api/v1/leaderboard/categoriesNo auth requiredCache: 60s

Returns per-category Elo rankings across all models.

ParameterTypeRequiredDescription
localestringnoFilter by locale code.

Example request

bash
curl https://http-actions.modelchorus.com/api/v1/leaderboard/categories
GET/api/v1/leaderboard/head-to-headNo auth requiredCache: 60s

Returns head-to-head win/loss statistics between all model pairs.

ParameterTypeRequiredDescription
modality"text" | "image"noFilter by battle modality. Defaults to "text" when omitted.
localestringnoFilter by locale code.
localeMode"exact" | "language" | "hybrid"noHow locale matching is applied. Default: exact.

Example request

bash
curl "https://http-actions.modelchorus.com/api/v1/leaderboard/head-to-head?modality=text"
GET/api/v1/leaderboard/localesNo auth requiredCache: 300s

Returns all locales that have battle data as string arrays.

ParameterTypeRequiredDescription
modality"text" | "image"noFilter by modality. Omit to return both text and image locale lists.

Example request

bash
curl https://http-actions.modelchorus.com/api/v1/leaderboard/locales

Example response

json
{
  "data": {
    "text": ["en", "es", "fr", "ar"],
    "image": ["en", "es"]
  }
}

Models

GET/api/v1/modelsNo auth requiredCache: 300s

Returns all available models with full metadata including provider, modality support, and configuration.

ParameterTypeRequiredDescription
localestringnoLocale context. Defaults to global.

Example request

bash
curl https://http-actions.modelchorus.com/api/v1/models

Example response

json
{
  "data": [
    {
      "id": "jn7abc123def456",
      "name": "GPT-4o",
      "modelId": "gpt-4o",
      "provider": "OpenAI",
      "inputModalities": ["text", "image"],
      "outputModalities": ["text"],
      "isPinned": true
    }
  ]
}
GET/api/v1/models/availabilityNo auth requiredCache: 300s

Returns a lightweight list of all models with name, modelId, and supported modalities. No stats or metadata.

Example request

bash
curl https://http-actions.modelchorus.com/api/v1/models/availability

Example response

json
{
  "data": [
    {
      "name": "GPT-4o",
      "modelId": "gpt-4o",
      "inputModalities": ["text", "image"],
      "outputModalities": ["text"]
    },
    {
      "name": "DALL-E 3",
      "modelId": "dall-e-3",
      "inputModalities": ["text"],
      "outputModalities": ["image"]
    }
  ]
}
GET/api/v1/models/:nameNo auth requiredCache: 300s

Returns a single model by its display name or modelId.

:name accepts either the human-readable display name or the modelId slug (e.g. gpt-4o).

Example request

bash
curl https://http-actions.modelchorus.com/api/v1/models/gpt-4o
GET/api/v1/models/:name/comparisonsNo auth requiredCache: 30s

Returns recent battle comparisons in which this model participated. User identity is stripped from all results.

ParameterTypeRequiredDescription
limitnumbernoNumber of comparisons to return. Range 1–50. Default 20.

Example request

bash
curl "https://http-actions.modelchorus.com/api/v1/models/gpt-4o/comparisons?limit=5"

Votes & Stats

GET/api/v1/votes/recentNo auth requiredCache: 15s

Returns the most recent votes that include user-submitted feedback (hasFeedback = true). This is a feedback-only feed — votes without comments are excluded. User identity is stripped from all results.

ParameterTypeRequiredDescription
limitnumbernoNumber of votes to return. Range 1–50. Default 20.

Example request

bash
curl https://http-actions.modelchorus.com/api/v1/votes/recent

Example response

json
{
  "data": [
    {
      "_id": "jn7abc123def456",
      "_creationTime": 1714000000000,
      "winner": "A",
      "userLocale": "en",
      "prompt": "Explain quantum entanglement.",
      "modality": "text",
      "feedback": "Model A was more concise.",
      "hasFeedback": true
    }
  ]
}
GET/api/v1/votes/feedbackNo auth requiredCache: 15s

Returns paginated votes that include user-submitted feedback text. User identity is stripped.

ParameterTypeRequiredDescription
limitnumbernoPage size. Range 1–50. Default 20.
cursorstringnoPagination cursor from the previous response.

Example request

bash
curl https://http-actions.modelchorus.com/api/v1/votes/feedback

Example response

json
{
  "data": [...],
  "pagination": { "hasMore": true, "nextCursor": "eyJ..." }
}
GET/api/v1/statsNo auth requiredCache: 60s

Returns platform-wide totals: total models, total votes cast, and vote breakdown by modality.

Example request

bash
curl https://http-actions.modelchorus.com/api/v1/stats

Example response

json
{
  "data": {
    "totalModels": 34,
    "totalTextModels": 28,
    "totalImageModels": 6,
    "totalTextVotes": 98432,
    "totalImageVotes": 12000,
    "totalVotes": 110432
  }
}
GET/api/v1/suggestionsNo auth requiredCache: 300s

Returns AI-generated prompt suggestions for the challenge UI, by locale and modality.

ParameterTypeRequiredDescription
localestringnoLocale for suggestions. Default: en.
modality"text" | "image"noSuggestion type. Default: text.

Example request

bash
curl "https://http-actions.modelchorus.com/api/v1/suggestions?locale=en&modality=text"

Battles

GET requires a standard-tier API key. POST endpoints require a write-tier API key.
POST/api/v1/battlesAPI key (write)

Create a new blind battle. Two anonymous models receive the prompt and generate responses in parallel.

ParameterTypeRequiredDescription
promptstringyesThe prompt to send. Max 10,000 characters.
modality"text" | "image"noBattle type. Default: "text".
localestringnoLocale context for model selection. Default: "en".

Example request

bash
curl -X POST https://http-actions.modelchorus.com/api/v1/battles \
  -H "Authorization: Bearer mc_0123456789abcdef0123456789abcdef" \
  -H "Content-Type: application/json" \
  -d '{"prompt": "Explain quantum entanglement simply.", "locale": "en"}'

Example response

json
{
  "data": {
    "id": "abc123",
    "status": "initializing",
    "pollUrl": "/api/v1/battles/abc123",
    "pollIntervalMs": 2000
  }
}
GET/api/v1/battles/:idAPI key (standard)

Poll the status of a battle. Returns per-turn responses, model names (hidden as 'Model A' / 'Model B' until voted), and vote status.

No cache. Poll until the top-level responsesReady is true before voting. Model names are revealed only after a vote is cast for that turn.

Example request

bash
curl https://http-actions.modelchorus.com/api/v1/battles/jn7abc123def456 \
  -H "Authorization: Bearer mc_0123456789abcdef0123456789abcdef"

Example response

json
{
  "data": {
    "id": "jn7abc123def456",
    "status": "active",
    "responsesReady": true,
    "modality": "text",
    "title": "Explain quantum entanglement simply.",
    "turnCount": 1,
    "votedCount": 0,
    "createdAt": 1714000000000,
    "comparisons": [
      {
        "id": "jn7cmp789xyz012",
        "turnNumber": 1,
        "status": "pending",
        "responsesReady": true,
        "modelA": "Model A",
        "modelB": "Model B",
        "responseA": "Quantum entanglement is a phenomenon where two particles...",
        "responseB": "Great question! When two particles become entangled...",
        "vote": null
      }
    ],
    "turns": [
      {
        "turnNumber": 1,
        "branchA": [
          { "role": "user", "content": "Explain quantum entanglement simply.", "messageIndex": 0 },
          { "role": "assistant", "content": "Quantum entanglement is...", "messageIndex": 1 }
        ],
        "branchB": [
          { "role": "user", "content": "Explain quantum entanglement simply.", "messageIndex": 0 },
          { "role": "assistant", "content": "Great question!...", "messageIndex": 1 }
        ]
      }
    ],
    "errorCode": null,
    "errorMessage": null
  }
}
POST/api/v1/battles/:id/voteAPI key (write)

Submit a vote for a completed battle. Reveals model identities in the response.

The API enforces a minimum 30-second wait from battle creation before a vote is accepted. Poll until responsesReady is true first, then vote.

ParameterTypeRequiredDescription
winner"A" | "B" | "tie" | "both_bad"yesWhich model you prefer, or indicate a tie or both bad.
feedbackstringnoOptional freeform feedback text.
ratingnumbernoOptional 1–5 star rating.
criteria{ accuracy?: number; helpfulness?: number; clarity?: number; creativity?: number }noOptional per-criterion scores (1–5 integer each).

Example request

bash
curl -X POST https://http-actions.modelchorus.com/api/v1/battles/jn7abc123def456/vote \
  -H "Authorization: Bearer mc_0123456789abcdef0123456789abcdef" \
  -H "Content-Type: application/json" \
  -d '{"winner": "A", "feedback": "Model A was more concise."}'

Example response

json
{
  "data": {
    "success": true,
    "modelsRevealed": {
      "modelA": "GPT-4o",
      "modelB": "Claude 3.5 Sonnet"
    }
  }
}
POST/api/v1/battles/:id/feedbackAPI key (write)

Attach feedback to a battle you have already voted on. At least one field required.

A vote must be cast for the battle before submitting feedback. At least one of feedback, rating, or criteria must be provided.

ParameterTypeRequiredDescription
feedbackstringnoFreeform feedback text.
ratingnumberno1–5 star rating.
criteria{ accuracy?: number; helpfulness?: number; clarity?: number; creativity?: number }noPer-criterion scores (1–5 integer each).

Example request

bash
curl -X POST https://http-actions.modelchorus.com/api/v1/battles/jn7abc123def456/feedback \
  -H "Authorization: Bearer mc_0123456789abcdef0123456789abcdef" \
  -H "Content-Type: application/json" \
  -d '{"feedback": "Both responses lacked citations."}'

Example response

json
{ "data": { "success": true } }
POST/api/v1/battles/:id/continueAPI key (write)

Continue a text battle with a follow-up prompt (multi-turn). Only supported for text modality — image battles are single-turn.

ParameterTypeRequiredDescription
promptstringyesFollow-up prompt. Max 10,000 characters.

Example request

bash
curl -X POST https://http-actions.modelchorus.com/api/v1/battles/jn7abc123def456/continue \
  -H "Authorization: Bearer mc_0123456789abcdef0123456789abcdef" \
  -H "Content-Type: application/json" \
  -d '{"prompt": "Now explain it to a 10-year-old."}'

Example response

json
{
  "data": {
    "turnNumber": 2,
    "status": "initializing",
    "pollUrl": "/api/v1/battles/jn7abc123def456",
    "pollIntervalMs": 2000
  }
}

Chats

GET requires a standard-tier API key. POST endpoints require a write-tier API key.
POST/api/v1/chatsAPI key (write)

Create a new single-model chat session. Use GET /api/v1/models/availability to list valid model identifiers.

Returns the session id only. pollIntervalMs is returned by POST /api/v1/chats/:id/message, not by this endpoint.

ParameterTypeRequiredDescription
modelIdstringyesModel identifier — display name or modelId slug. Use /models/availability to enumerate.
localestringnoLocale context. Default: "en".
modality"text" | "image"noSession modality. Default: "text".

Example request

bash
curl -X POST https://http-actions.modelchorus.com/api/v1/chats \
  -H "Authorization: Bearer mc_0123456789abcdef0123456789abcdef" \
  -H "Content-Type: application/json" \
  -d '{"modelId": "gpt-4o", "locale": "en"}'

Example response

json
{
  "data": {
    "id": "jn7cht456uvw789",
    "modelName": "GPT-4o"
  }
}
GET/api/v1/chats/:idAPI key (standard)

Poll the current state of a chat session, including all messages and streaming status.

No cache — intended for real-time polling. Use the pollIntervalMs returned by POST /api/v1/chats/:id/message.

ParameterTypeRequiredDescription
cursorstringnoPagination cursor to fetch messages after a given point.

Example request

bash
curl https://http-actions.modelchorus.com/api/v1/chats/jn7cht456uvw789 \
  -H "Authorization: Bearer mc_0123456789abcdef0123456789abcdef"

Example response

json
{
  "data": {
    "id": "jn7cht456uvw789",
    "modelName": "GPT-4o",
    "modelId": "gpt-4o",
    "modality": "text",
    "messageCount": 2,
    "isActive": true,
    "createdAt": 1714000000000,
    "messages": [
      { "id": "msg_001", "role": "user", "content": "What is the capital of France?", "createdAt": 1714000001000 },
      { "id": "msg_002", "role": "assistant", "content": "The capital of France is Paris.", "createdAt": 1714000003000 }
    ],
    "pagination": { "hasMore": false, "nextCursor": null }
  }
}
POST/api/v1/chats/:id/messageAPI key (write)

Send a new message to a chat session. Poll the session after sending to retrieve the response.

ParameterTypeRequiredDescription
messagestringyesThe message text to send. Max 10,000 characters.

Example request

bash
curl -X POST https://http-actions.modelchorus.com/api/v1/chats/jn7cht456uvw789/message \
  -H "Authorization: Bearer mc_0123456789abcdef0123456789abcdef" \
  -H "Content-Type: application/json" \
  -d '{"message": "What is the capital of France?"}'

Example response

json
{
  "data": {
    "status": "processing",
    "messageCount": 2,
    "pollUrl": "/api/v1/chats/jn7cht456uvw789",
    "pollIntervalMs": 2000
  }
}

Changelog

v1.0 — April 2026

  • Initial public release.
  • 21 endpoints across leaderboard, models, votes, battles, and chats.
  • Four auth tiers: anonymous, standard, read (partner rate limits, no write access), write.