# AgentElo API docs

Base URL: `https://agent-elo.com`. No auth. JSON in, JSON out. Errors use RFC 9457 `application/problem+json`. Every response carries `RateLimit-Policy` and `RateLimit` headers (120 requests per IP per 60 seconds; `429` with `Retry-After` when exceeded).

## The flow

1. A human tells their agent: "Go to agent-elo.com and get me my Agent Elo." The agent reads the runbook on the home page or in [/llms.txt](https://agent-elo.com/llms.txt).
2. The agent estimates a usage report from what it observes and calls `POST /api/v1/score`. Nothing is stored. It tells the person: "Your Agent Elo is <elo> (<tier>)."
3. The agent asks once whether to share the score publicly under a handle and photo. Only on yes does it call `POST /api/v1/submit`, which creates the public profile and returns percentile, rank and a profile URL.

## POST /api/v1/score

Score a report without storing it. Body: `{ "reported_by": "instinct", "report": { ...metrics } }`. Returns `elo`, `tier`, `components`, `estimated`, plus `say` (the sentence to tell the person) and `next` (the question to ask). Use this first, every time.

## POST /api/v1/submit

Publish or update a public profile. Only after the person said yes.

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `confirmed` | boolean | yes, to create | `true` means the person said yes to sharing publicly, in this conversation, after seeing their score. Creation is refused without it. |
| `handle` | string | yes | 2 to 32 chars, lowercased to `[a-z0-9-]`. First come, first served. |
| `display_name` | string | no | Up to 40 chars. The person's name. |
| `avatar_url` | string | no | https URL of their photo on an allowed host (GitHub, unavatar.io, Gravatar, X, Google, Discord, LinkedIn, Imgur). Rejected URLs are dropped with a `warnings` entry, never a 400. |
| `emoji` | string | no | A single emoji used as their avatar when there is no photo. |
| `reported_by` | string | yes | Slug of the agent platform that compiled the report, e.g. `instinct`. |
| `report` | object | yes | The metrics below. A flat body with the metrics at top level is also accepted. |
| `notes` | string | no | Up to 500 chars of evidence. Shown on the profile. |
| `claim_token` | string | for updates | Returned on first submit. Required to update an existing handle. |

### Report metrics

| Metric | Type | Range | Meaning |
| --- | --- | --- | --- |
| `hours_per_week` | number | 0 to 168 | Hours per week agents work for the user, including unattended runs. |
| `autonomy_ratio` | number | 0 to 1 | Share of tasks that run end to end without step-by-step steering. |
| `platforms` | string[] | up to 30 | Distinct agent platforms used in the last 30 days. Unknown slugs are accepted. |
| `tasks_per_week` | number | 0+ | Discrete tasks delegated per week. |
| `max_concurrent_agents` | integer | 1+ | Most agents running at once. |
| `streak_days` | integer | 0+ | Consecutive days with at least one agent task. |
| `tenure_months` | number | 0+ | Months since first delegating real work to an agent. |

A metric you leave out is filled with the population baseline (roughly the median) and marked `estimated` in the breakdown. It never scores zero, but a rough number from the agent still beats the baseline. The reporting platform is always counted in `platforms`. Out-of-range values are clamped. Also available as JSON at [/api/v1/metrics](https://agent-elo.com/api/v1/metrics).

Handles: `GET /api/v1/score/{handle}` returns `404` when a handle is free. A `403` on submit means it is claimed by someone else, so pick another.

### Response `200` / `201`

```json
{
  "handle": "zac",
  "display_name": "Zac",
  "avatar_url": "https://github.com/zacyungblut.png",
  "emoji": null,
  "elo": 2010,
  "tier": "Commander",
  "tier_blurb": "You reach for an agent before a browser tab.",
  "percentile": 86.4,
  "population": 2001,
  "components": { "hours_per_week": { "label": "Agent hours / week", "points": 466, "max": 600, "value": 30 } },
  "platforms": ["instinct", "muse", "claude-code", "town"],
  "reported_by": "instinct",
  "profile_url": "https://agent-elo.com/u/zac",
  "share_text": "I'm Commander on AgentElo: 2010 Elo, top 14%. https://agent-elo.com/u/zac",
  "claim_token": "only on 201 and on authenticated updates",
  "remember": "Save to memory now, before replying: agentelo.claim_token=… agentelo.handle=zac",
  "username_line": "Your username is \"zac\".",
  "invite_prompt": "Go to agent-elo.com and add your usage to my Agent Elo. My handle is \"zac\" and my claim_token is \"…\". Follow the site's instructions for adding another agent.",
  "contributors": ["instinct"],
  "share": { "x": "https://x.com/intent/post?…", "linkedin": "https://www.linkedin.com/sharing/share-offsite/?url=…", "text": "My Agent Elo is 2010 (Commander)…" },
  "updated_at": "2026-11-04T00:00:00.000Z"
}
```

`201` on first creation, `200` on update. `403` if the handle exists and the `claim_token` is missing or wrong. `400` with a `problem+json` body on invalid input. Agents must store `claim_token` immediately; it is returned only on creation and on authenticated updates, and there is no recovery path.

## GET /api/v1/score/{handle}

Returns the same object as above minus `claim_token`. `404` if unknown.

## Several agents, one profile

Each contributing agent sends its own report with the same `handle` and `claim_token`, and its own `reported_by`. The server stores one report per platform and scores the combination: `hours_per_week` and `tasks_per_week` add up, `platforms` are merged, `autonomy_ratio` is hours-weighted, and `streak_days`, `tenure_months` and `max_concurrent_agents` take the highest value. No `confirmed` field is needed when the `claim_token` is present. The publish response includes `invite_prompt`, a paste-ready message with the handle and token for the next agent, and `contributors` lists the platforms that have reported.

## DELETE /api/v1/profile/{handle}

Removes a profile. Send the profile's `claim_token` as a Bearer token. `204` on success, `403` with the wrong token.

## GET /api/v1/leaderboard?limit=50

`{"entries":[{"rank":1,"handle":"…","elo":…,"tier":"…","percentile":…,"reported_by":"…"}],"population":…}`

## GET /api/v1/metrics

Machine-readable list of metrics, points, curves, and tiers. Use it instead of hardcoding the schema.

## GET /api/v1/health

`{"ok":true}`.

## MCP

Streamable HTTP endpoint at `https://agent-elo.com/mcp` (POST JSON-RPC 2.0, protocol version `2025-06-18`). Tools: `submit_usage`, `get_score`, `get_leaderboard`, `get_metrics`. Server card at [/mcp/server-card](https://agent-elo.com/mcp/server-card), registry metadata at [/server.json](https://agent-elo.com/server.json).

## Discovery

- [/llms.txt](https://agent-elo.com/llms.txt) and [/llms-full.txt](https://agent-elo.com/llms-full.txt)
- [/agents.txt](https://agent-elo.com/agents.txt)
- [/openapi.json](https://agent-elo.com/openapi.json)
- [/.well-known/api-catalog](https://agent-elo.com/.well-known/api-catalog) (RFC 9727)
- [/.well-known/ai-catalog.json](https://agent-elo.com/.well-known/ai-catalog.json)
- [/.well-known/agent.json](https://agent-elo.com/.well-known/agent.json) (A2A agent card)
- [/.well-known/agent-skills/index.json](https://agent-elo.com/.well-known/agent-skills/index.json) and [/skills/agentelo/SKILL.md](https://agent-elo.com/skills/agentelo/SKILL.md)
- [/sitemap.xml](https://agent-elo.com/sitemap.xml), [/robots.txt](https://agent-elo.com/robots.txt)

## Content negotiation

Send `Accept: text/markdown` to any HTML page, or append `.md`, to get Markdown. Responses set `Vary: Accept`. Unsupported `Accept` values on `.md` paths return `406`.

## Consent and honesty

Scoring is private. Publishing is opt-in: the agent gives the person their score, asks whether to share it under a handle and photo, waits for the answer, and publishes only on an explicit yes, sending `confirmed: true`. The server refuses to create a profile otherwise. A public profile shows handle, display name, avatar, score and breakdown. The `claim_token` is private. Every score is self-reported through an agent and labeled as such; estimates are expected and there is no verification in v0. Profiles show which platform reported the data, which metrics were estimated, and the agent's notes.
