# nades.ai partner API

Examples show the shape of an answer. The names and numbers in them are made up, and a field written "..." stands for a block cut from the example.

## What this is

Pro CS2 utility statistics, lineups, combos and matches, as a JSON API and an MCP server. One key opens both.

Each query is a GET on REST and a tool on MCP with the same parameters. Parameters not listed are refused. Teams, players and maps are named by slug or by name.

Terms as agreed with nades.ai. Questions: support@nades.ai

This reference is the page https://nades.ai/partner-api as plain text.

### Changelog

- `v1` Twenty queries over REST and MCP.

First call:

````
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://api.nades.ai/api/partner/v1/find?q=example&kind=team"
````

## Authentication

Send the key on every call, as `X-API-Key` or as `Authorization: Bearer`.

The REST base is `https://api.nades.ai/api/partner/v1/`, one GET per query. Every parameter goes in the query string.

Request:

````
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://api.nades.ai/api/partner/v1/find?q=example&kind=team"
````

## Limits

500 calls a day and 30 a minute per key, unless your key says otherwise. A call is one REST request or one MCP `tools/call`. Only that spends the key's limits: `initialize`, `server/discover`, `tools/list`, `ping` and every notification do not.

A refused call answers 429. Its body names the limit (`minute`, `day` or `ip`), its max and when it resets.

A refused call:

````
{
  "success": false,
  "message": "Per-minute limit of 30 calls reached; resets at 2026-01-01T12:01:00Z.",
  "error": "Per-minute limit of 30 calls reached; resets at 2026-01-01T12:01:00Z.",
  "code": "RATE_LIMITED",
  "data": {
    "limit": "minute",
    "max": 30,
    "resetsAt": "2026-01-01T12:01:00Z"
  }
}
````

## Freshness and windows

Answers come from the site's current release, which is rebuilt after new matches are analysed. `meta.dataUpdatedAt` says when the release went live and `meta.asOf` the instant the windows count back from.

A window counts games whose match started at or after `meta.window.from`. The values are 1m, 3m, 6m, 1y, all. Where a query's table states a default it is 3m, and the site's boards default to 6m, so a figure can differ from the page until the window is set to match. Not every query takes every window: `lineups` takes 3m or all, `map` always reads the last three months, and `lineup` has no default, so left out its answer states no window.

`meta.definitions` states, in words, how each figure in the answer is computed: floors, small-sample adjustment, win rates and coverage.

meta in an answer:

````
{
  "releaseId": "example-release",
  "dataUpdatedAt": "2026-01-01T06:00:00Z",
  "asOf": "2026-01-01T06:00:00Z",
  "window": null,
  "mapsCovered": [],
  "credit": "Data: nades.ai — https://nades.ai",
  "definitions": {},
  "links": {
    "docs": "https://nades.ai/partner-api"
  }
}
````

## Answers

Every answer is one envelope: apiVersion, query, params, data and meta. Names are resolved for you, and params echo what was used. The example here shows the shape; its names and numbers are made up.

Teams and players named in an answer are objects of `name`, `slug` and `url`; a slug and a url are null where the site has no page for them.

REST is never cut. An MCP answer over 60 KB is cut to fit: every list the query can cut keeps the same number of rows, and the answer carries `truncated` and a note after any note it had, such as "Cut `lineups` to 19 of 30 rows and `combos` to 19 of 20 rows to fit one answer. Narrow with `family`, `lineups` or `combos`." A list that pages adds no sentence and names where to read on: `nextOffset` on lineups and group, `nextCursor` on matches, set for the last row kept. An answer with nothing to cut goes out whole, with a note saying it is past the 60 KB. Narrow it with the parameters the note names. The wire carries an MCP answer twice, as text and as a structured copy, so it is about double; the 60 KB holds for each copy.

A field that arrives with the next site rebuild answers null beside a note that says so, never an error and never a zero. The data holds played dates only. It has no tournament tier, no LAN or online flag, no prize money and no official dates.

Request:

````
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://api.nades.ai/api/partner/v1/find?q=example&kind=team"
````

Example answer:

````
{
  "apiVersion": "v1",
  "query": "find",
  "params": {
    "q": "example",
    "kind": "team"
  },
  "data": {
    "matches": [
      {
        "kind": "team",
        "name": "Example Team",
        "slug": "example-team",
        "url": "https://nades.ai/teams/example-team",
        "worldRank": 12,
        "lastPlayedAt": "2026-01-01T12:00:00.000Z",
        "currentTeam": null,
        "via": "name"
      }
    ],
    "total": 1
  },
  "meta": {
    "releaseId": "example-release",
    "dataUpdatedAt": "2026-01-01T06:00:00Z",
    "asOf": "2026-01-01T06:00:00Z",
    "window": null,
    "mapsCovered": [],
    "credit": "Data: nades.ai — https://nades.ai",
    "definitions": {},
    "links": {
      "docs": "https://nades.ai/partner-api"
    }
  }
}
````

## Errors

An error is JSON with success false, a plain sentence in `message`, a `code` and, where it helps, `data`.

| Code | Status | Meaning |
| --- | --- | --- |
| `API_KEY_REQUIRED` | 401 | No key was sent. |
| `API_KEY_INVALID` | 401 | The key is not valid. |
| `API_KEY_REVOKED` | 401 | The key was revoked. |
| `RATE_LIMITED` | 429 | A limit was reached. data names the limit, its max and resetsAt. |
| `NOT_FOUND` | 404 | No such team, player, map or page. data.suggestions lists near names. |
| `AMBIGUOUS_NAME` | 409 | A name fits several. data.candidates lists them, by slug, or by id for events. |
| `MAP_NOT_COVERED` | 404 | The map is not covered. data.mapsCovered lists those that are. |
| `BAD_REQUEST` | 400 | A parameter is wrong, a cursor included. data.param names it, data.allowed its values. |
| `BAND_REQUIRED` | 400 | flashToKill for players needs topTeams. |
| `NOT_BUILT` | 503 | No site release is live yet. Try again later. |
| `INTERNAL` | 500 | Something failed on our side. Try again. |

An error:

````
{
  "success": false,
  "message": "No team named 'exmple'. Did you mean Example Team?",
  "error": "No team named 'exmple'. Did you mean Example Team?",
  "code": "NOT_FOUND",
  "data": {
    "suggestions": [
      {
        "name": "Example Team",
        "slug": "example-team",
        "url": "https://nades.ai/teams/example-team",
        "rank": 12
      }
    ]
  }
}
````

## Credit and links

Terms as agreed with nades.ai. Questions: support@nades.ai

Credit: `Data: nades.ai` with a link to the page the answer names. Every answer carries its links and a `meta.credit` line.

Credit and links in an answer:

````
{
  "credit": "Data: nades.ai — https://nades.ai",
  "links": {
    "docs": "https://nades.ai/partner-api"
  }
}
````

## MCP setup

The server is `https://api.nades.ai/api/partner/mcp`, over streamable HTTP, stateless. Send the key as a header, as on REST. A client that cannot set headers cannot connect.

Every request must send `Accept: application/json, text/event-stream`, or the server answers 406. The answer is SSE-framed: one `message` event whose data is the JSON-RPC reply. Common MCP clients do this on their own.

A tool call that fails returns the error body under [Errors](#errors) with `isError: true`.

Client config:

````
{
  "mcpServers": {
    "nades-ai": {
      "type": "http",
      "url": "https://api.nades.ai/api/partner/mcp",
      "headers": {
        "X-API-Key": "YOUR_API_KEY"
      }
    }
  }
}
````

List the tools:

````
curl -X POST https://api.nades.ai/api/partner/mcp \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
````

## Find a name

GET `/v1/find`, MCP tool `nades_find`

Find a team or player by part of the name.

| Name | Need | Allowed values | What it does |
| --- | --- | --- | --- |
| `q` | required | 2 to 64 characters | Part of the name to look for. |
| `kind` | optional | team, player, event or any (default any) | What to look for. |

Every other query takes the slug it returns. With kind event it finds tournaments.

kind event lists editions, not competitions: each row has id (the competition), editionId, name, competitionName, firstPlayedAt, lastPlayedAt and series, and no slug or link. any never holds events. The exact name comes first, then the newest. Up to 30 rows, with data.total and a data.note when there are more.

The answer holds the closest 30 names, 15 of each kind on any, in rank order. data.total counts every name that matches, and data.note says when the list stops short of it. A team or player with no published lineup is matched only by the letters of its slug; when nothing matches and such names exist, data.note says so.

Request:

````
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://api.nades.ai/api/partner/v1/find?q=example&kind=team"
````

MCP tools/call:

````
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "nades_find",
    "arguments": {
      "q": "example",
      "kind": "team"
    }
  }
}
````

Example answer:

````
{
  "apiVersion": "v1",
  "query": "find",
  "params": {
    "q": "example",
    "kind": "team"
  },
  "data": {
    "matches": [
      {
        "kind": "team",
        "name": "Example Team",
        "slug": "example-team",
        "url": "https://nades.ai/teams/example-team",
        "worldRank": 12,
        "lastPlayedAt": "2026-01-01T12:00:00.000Z",
        "currentTeam": null,
        "via": "name"
      }
    ],
    "total": 1
  },
  "meta": {
    "releaseId": "example-release",
    "dataUpdatedAt": "2026-01-01T06:00:00Z",
    "asOf": "2026-01-01T06:00:00Z",
    "window": null,
    "mapsCovered": [],
    "credit": "Data: nades.ai — https://nades.ai",
    "definitions": {},
    "links": {
      "docs": "https://nades.ai/partner-api"
    }
  }
}
````

## List teams

GET `/v1/teams`, MCP tool `nades_teams`

List teams in rank order.

| Name | Need | Allowed values | What it does |
| --- | --- | --- | --- |
| `topTeams` | optional | 10, 20 or 30 | Keeps the HLTV top teams. |
| `q` | optional | part of a name | Keeps the names that fit. |
| `rosters` | optional | true or false (default false) | true includes rosters. |
| `limit` | optional | 1 to 50 (default 30) | Rows on one page; at most 30 with rosters. |
| `cursor` | optional | from the previous answer | Reads the next page. |

With q and no other narrowing, the pages hold only the closest 200 matches, and data.note says when there are more. A team or player with no published lineup is matched only by the letters of its slug.

Request:

````
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://api.nades.ai/api/partner/v1/teams?topTeams=10&limit=2"
````

MCP tools/call:

````
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "nades_teams",
    "arguments": {
      "topTeams": 10,
      "limit": 2
    }
  }
}
````

Example answer:

````
{
  "data": {
    "teamsInBand": 9,
    "teams": [
      {
        "name": "Example Team",
        "slug": "example-team",
        "url": "https://nades.ai/teams/example-team",
        "logoUrl": "https://assets.nades.ai/example-logo.png",
        "worldRank": 1,
        "seriesInData": 40,
        "firstPlayedAt": "2025-06-01T12:00:00.000Z",
        "lastPlayedAt": "2026-01-01T12:00:00.000Z"
      }
    ]
  }
}
````

## One team

GET `/v1/team`, MCP tool `nades_team`

One team: roster, place on every board, form, round and side record, latest matches and best lineups.

| Name | Need | Allowed values | What it does |
| --- | --- | --- | --- |
| `team` | required | a slug or a name; call find when unsure | The team. |
| `map` | optional | one map, as mirage or de_mirage | Adds the team's place on that map's boards, and reads its round and side record and best lineups there; the record then states no series. Roster, form, latest matches and the all-maps places ignore it. |
| `window` | optional | 1m, 3m, 6m, 1y or all (default 3m) | How far back the record, the roster's utility and the places on the boards count. The best lineups read 3m or all only: any other window reads 3m there, and lineupsWindow says which. Latest matches take no window, and form is always 1m against 3m. |
| `lineups` | optional | 0 to 8 (default 8) | Best lineups in the answer. |
| `matches` | optional | 0 to 10 (default 5) | Latest matches in the answer. |

record.buyMaps is the number of maps the buy figures cover, or null. record.byBuy is null unless buyMaps equals maps, and record.byBuyReason then says why: buy_maps_short or buy_maps_unknown. The same holds for each row of perMap and for the records in compare.

Request:

````
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://api.nades.ai/api/partner/v1/team?team=example-team&window=3m&lineups=1&matches=1"
````

MCP tools/call:

````
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "nades_team",
    "arguments": {
      "team": "example-team",
      "window": "3m",
      "lineups": 1,
      "matches": 1
    }
  }
}
````

Example answer:

````
{
  "data": {
    "team": {
      "name": "Example Team",
      "slug": "example-team",
      "url": "https://nades.ai/teams/example-team",
      "worldRank": 1,
      "seriesInData": 40,
      "seriesWon": 28,
      "games": 90,
      "publishedLineups": 120,
      "mapsPlayed": [
        {
          "map": "example-map",
          "matchMaps": 30
        }
      ],
      "roster": [
        {
          "name": "examplePlayer",
          "slug": "exampleplayer",
          "url": "https://nades.ai/players/exampleplayer",
          "lastPlayedForTeamAt": "2026-01-01T12:00:00.000Z"
        }
      ],
      "rosterNote": "Roster is the five most recent players who threw for the team, not a signed list."
    },
    "standings": {
      "allMaps": [
        {
          "stat": "flashKills",
          "window": "3m",
          "url": "https://nades.ai/leaderboards/example",
          "rank": 4,
          "of": 40,
          "value": 0.081,
          "games": 52,
          "numerator": 210,
          "denominator": 2600,
          "reason": "ranked"
        }
      ]
    },
    "trend": [
      {
        "stat": "flashKills",
        "value1m": 0.09,
        "value3m": 0.081,
        "rank1m": 3,
        "rank3m": 4,
        "direction": "up"
      }
    ],
    "record": {
      "maps": 30,
      "mapsWon": 19,
      "mapWinPct": 0.633,
      "rounds": 640,
      "roundsWon": 340,
      "roundWinPct": 0.531,
      "sides": {
        "t": {
          "played": 310,
          "won": 150,
          "pct": 0.484
        },
        "ct": {
          "played": 322,
          "won": 185,
          "pct": 0.575
        },
        "coverage": 0.988,
        "refused": 1
      },
      "buyMaps": 29,
      "byBuy": null,
      "byBuyReason": "buy_maps_short",
      "series": {
        "played": 12,
        "won": 8
      },
      "perMap": [
        {
          "map": "example-map",
          "maps": 8,
          "mapsWon": 5,
          "mapWinPct": 0.625,
          "rounds": 170,
          "roundsWon": 92,
          "roundWinPct": 0.541,
          "sides": {
            "t": {
              "played": 82,
              "won": 40,
              "pct": 0.488
            },
            "ct": {
              "played": 88,
              "won": 52,
              "pct": 0.591
            },
            "coverage": 1,
            "refused": 0
          },
          "buyMaps": 8,
          "byBuy": {
            "pistol": {
              "t": {
                "played": 16,
                "won": 7,
                "pct": 0.438
              },
              "ct": {
                "played": 16,
                "won": 9,
                "pct": 0.563
              }
            },
            "eco": {
              "t": {
                "played": 9,
                "won": 2,
                "pct": null,
                "reason": "small_sample"
              },
              "ct": {
                "played": 11,
                "won": 3,
                "pct": 0.273
              }
            },
            "force": {
              "t": {
                "played": 20,
                "won": 9,
                "pct": 0.45
              },
              "ct": {
                "played": 18,
                "won": 9,
                "pct": 0.5
              }
            },
            "full": {
              "t": {
                "played": 37,
                "won": 22,
                "pct": 0.595
              },
              "ct": {
                "played": 43,
                "won": 31,
                "pct": 0.721
              }
            }
          }
        }
      ]
    },
    "roundsBySide": {
      "maps": 30,
      "rounds": 640,
      "t": 318,
      "ct": 322,
      "sideUnknown": 0
    },
    "latestMatches": [
      {
        "seriesId": "example-series",
        "playedAt": "2026-01-01T12:00:00.000Z",
        "tournament": "Example Cup",
        "format": "bo3",
        "url": "https://nades.ai/matches/example-series",
        "score": {
          "team": 2,
          "opponent": 0
        }
      }
    ],
    "lineupsWindow": "3m",
    "lineups": [
      {
        "lineupId": "example-lineup-id",
        "title": "Example Map A Site Smoke from Example Spot",
        "url": "https://nades.ai/example-map/smokes/example-lineup",
        "family": "smoke",
        "side": "t",
        "technique": "jumpthrow",
        "from": "Example Spot",
        "to": "A Site",
        "insta": false,
        "usage": {
          "...": "usage figures, cut from this example"
        },
        "video": {
          "url": "https://assets.nades.ai/example.mp4"
        },
        "image": {
          "release": "https://assets.nades.ai/example.jpg"
        },
        "team": {
          "throws": 40,
          "rounds": 30,
          "lastThrownAt": "2026-01-01T12:00:00.000Z"
        }
      }
    ],
    "lineupsNote": "Best order, as the team's page lists them. Use nades_lineups to sort by most thrown or filter."
  }
}
````

## List players

GET `/v1/players`, MCP tool `nades_players`

List players in rank order.

| Name | Need | Allowed values | What it does |
| --- | --- | --- | --- |
| `topTeams` | optional | 10, 20 or 30 | Keeps the players of the HLTV top teams. |
| `teams` | optional | up to 30, comma separated on REST, an array on MCP | Keeps the players of these teams. A name that does not resolve is listed in data.unresolved. |
| `q` | optional | part of a name | Keeps the names that fit. |
| `limit` | optional | 1 to 50 (default 50) | Rows on one page. |
| `cursor` | optional | from the previous answer | Reads the next page. |

With q and no other narrowing, the pages hold only the closest 200 matches, and data.note says when there are more. A team or player with no published lineup is matched only by the letters of its slug.

Request:

````
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://api.nades.ai/api/partner/v1/players?teams=example-team"
````

MCP tools/call:

````
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "nades_players",
    "arguments": {
      "teams": [
        "example-team"
      ]
    }
  }
}
````

Example answer:

````
{
  "data": {
    "unresolved": [],
    "players": [
      {
        "name": "examplePlayer",
        "slug": "exampleplayer",
        "url": "https://nades.ai/players/exampleplayer",
        "portraitUrl": null,
        "countryCode": "XX",
        "currentTeam": {
          "name": "Example Team",
          "slug": "example-team",
          "url": "https://nades.ai/teams/example-team",
          "worldRank": 1
        },
        "seriesInData": 38,
        "lastPlayedAt": "2026-01-01T12:00:00.000Z"
      }
    ]
  }
}
````

## One player

GET `/v1/player`, MCP tool `nades_player`

One player: utility rates and sums, smoke breaks among them, place on every board, form, latest matches and best lineups.

| Name | Need | Allowed values | What it does |
| --- | --- | --- | --- |
| `player` | required | a slug or a name; call find when unsure | The player. |
| `team` | optional | a slug or a name | Looks for the player among that team's current players only, which narrows a shared name. |
| `map` | optional | one map, as mirage or de_mirage | Adds the player's place on that map's boards, and reads the best lineups there. Utility rates and sums, form, latest matches and the all-maps places ignore it. |
| `window` | optional | 1m, 3m, 6m, 1y or all (default 3m) | How far back utility and the places on the boards count; utilityByWindow always holds every window. The best lineups read 3m or all only: any other window reads 3m there, and lineupsWindow says which. Latest matches take no window, and form is always 1m against 3m. |
| `lineups` | optional | 0 to 8 (default 8) | Best lineups in the answer. |
| `matches` | optional | 0 to 5 (default 5) | Latest matches in the answer. |

utility.smokeBreaks holds hes (HEs that only broke a smoke and hit no one), perHundredRounds, kills (through those smokes, his own and his teammates) and ownKills, counted on the games whose replay file was measured for smoke breaks. It carries partial and a note where fewer of his games were measured than he played, and is null on a release built before smoke breaks. utility.perGrenade.heDamage leaves the smoke breaks out.

Request:

````
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://api.nades.ai/api/partner/v1/player?player=exampleplayer&window=3m&lineups=1&matches=1"
````

MCP tools/call:

````
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "nades_player",
    "arguments": {
      "player": "exampleplayer",
      "window": "3m",
      "lineups": 1,
      "matches": 1
    }
  }
}
````

Example answer:

````
{
  "data": {
    "player": {
      "name": "examplePlayer",
      "slug": "exampleplayer",
      "url": "https://nades.ai/players/exampleplayer",
      "countryCode": "XX",
      "currentTeam": {
        "name": "Example Team",
        "slug": "example-team",
        "url": "https://nades.ai/teams/example-team",
        "worldRank": 1
      },
      "formerTeam": null,
      "games": 88,
      "publishedLineups": 30,
      "lastPlayedAt": "2026-01-01T12:00:00.000Z"
    },
    "utility": {
      "...": "the window's utility figures, cut from this example"
    },
    "utilityByWindow": [],
    "utilityByTeam": [],
    "standings": {
      "allMaps": [
        {
          "stat": "flashKills",
          "window": "3m",
          "url": "https://nades.ai/leaderboards/example",
          "rank": 4,
          "of": 40,
          "value": 0.081,
          "games": 52,
          "numerator": 210,
          "denominator": 2600,
          "reason": "ranked"
        }
      ]
    },
    "trend": [],
    "latestMatches": [
      {
        "seriesId": "example-series",
        "playedAt": "2026-01-01T12:00:00.000Z",
        "tournament": "Example Cup",
        "format": "bo3",
        "url": "https://nades.ai/matches/example-series",
        "score": {
          "team": 2,
          "opponent": 0
        }
      }
    ],
    "lineupsWindow": "3m",
    "lineups": [
      {
        "lineupId": "example-lineup-id",
        "title": "Example Map A Site Smoke from Example Spot",
        "url": "https://nades.ai/example-map/smokes/example-lineup",
        "family": "smoke",
        "side": "t",
        "technique": "jumpthrow",
        "from": "Example Spot",
        "to": "A Site",
        "insta": false,
        "usage": {
          "...": "usage figures, cut from this example"
        },
        "video": {
          "url": "https://assets.nades.ai/example.mp4"
        },
        "image": {
          "release": "https://assets.nades.ai/example.jpg"
        },
        "player": {
          "throws": 25,
          "lastThrownAt": "2026-01-01T12:00:00.000Z"
        }
      }
    ]
  }
}
````

## Compare two teams

GET `/v1/compare`, MCP tool `nades_compare_teams`

Two teams side by side, with their series against each other.

| Name | Need | Allowed values | What it does |
| --- | --- | --- | --- |
| `teamA, teamB` | required | a slug or a name | The two teams. |
| `map` | optional | one map, as mirage or de_mirage | Reads each side by side board, record and best lineups on that map. Latest matches and head to head ignore it, as they ignore the window. |
| `window` | optional | 1m, 3m, 6m, 1y or all (default 3m) | How far back each record and board counts. The best lineups read 3m or all only: any other window reads 3m there, and lineupsWindow says which. Latest matches take no window. |
| `lineups` | optional | 0 to 8 (default 5) | Best lineups in the answer, for each team. |
| `matches` | optional | 0 to 10 (default 5) | Latest matches in the answer, for each team. |
| `topTeams` | optional | 10, 20 or 30 | Reads the side by side boards within the HLTV top teams; flashToKill stays on the board of every team. |

headToHead counts every series between the two teams in the data, whatever window or map is asked.

Request:

````
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://api.nades.ai/api/partner/v1/compare?teamA=example-team&teamB=other-team&window=3m"
````

MCP tools/call:

````
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "nades_compare_teams",
    "arguments": {
      "teamA": "example-team",
      "teamB": "other-team",
      "window": "3m"
    }
  }
}
````

Example answer:

````
{
  "data": {
    "a": {
      "team": {
        "name": "Example Team",
        "slug": "example-team",
        "url": "https://nades.ai/teams/example-team"
      },
      "...": "the same blocks as the team query, cut from this example"
    },
    "b": {
      "team": {
        "name": "Other Team",
        "slug": "other-team",
        "url": "https://nades.ai/teams/other-team"
      },
      "...": "the same blocks as the team query, cut from this example"
    },
    "sideBySide": {
      "boards": [
        {
          "stat": "flashKills",
          "map": null,
          "a": {
            "rank": 4,
            "of": 40,
            "value": 0.081,
            "reason": "ranked"
          },
          "b": {
            "rank": 9,
            "of": 40,
            "value": 0.066,
            "reason": "ranked"
          },
          "edge": "a"
        }
      ],
      "record": {
        "a": {
          "maps": 30,
          "mapsWon": 19,
          "mapWinPct": 0.633,
          "rounds": 640,
          "roundsWon": 340,
          "roundWinPct": 0.531,
          "sides": null,
          "reason": "coverage_below_floor",
          "coverage": 0.62,
          "refused": 9,
          "buyMaps": 21,
          "byBuy": null,
          "series": {
            "played": 12,
            "won": 8
          },
          "perMap": []
        },
        "b": {
          "...": "the same record block, cut from this example"
        }
      }
    },
    "headToHead": {
      "window": "all",
      "seriesInData": 1,
      "won": {
        "a": 1,
        "b": 0
      },
      "series": [
        {
          "seriesId": "example-series",
          "playedAt": "2026-01-01T12:00:00.000Z",
          "tournament": "Example Cup",
          "score": {
            "a": 2,
            "b": 0
          },
          "maps": [
            {
              "map": "example-map",
              "score": {
                "a": 13,
                "b": 7
              }
            }
          ],
          "url": "https://nades.ai/matches/example-series"
        }
      ]
    }
  }
}
````

## Leaderboard

GET `/v1/leaderboard`, MCP tool `nades_leaderboard`

Rank teams or players on one utility stat.

| Name | Need | Allowed values | What it does |
| --- | --- | --- | --- |
| `stat` | required | flashKills, blindTime, heDamage, smokeBreaks, smokeBreakKills, molotovDamage, nadesPerRound, smokesPerRound or flashToKill | The stat to rank on. flashToKill with kind player needs topTeams, else BAND_REQUIRED. |
| `kind` | optional | team or player (default team) | Ranks teams or players. |
| `window` | optional | 1m, 3m, 6m, 1y or all (default 3m) | How far back games count. |
| `map` | optional | one map, as mirage or de_mirage | The board of that map alone. Left out, all maps. |
| `topTeams` | optional | 10, 20 or 30 | Ranks within the HLTV top teams. |
| `limit` | optional | 1 to 100 (default 10) | Rows in data.best, and in data.worst. |
| `worst` | optional | true or false (default false) | true adds data.worst, the lowest rows, lowest first, beside data.best, which stays. It is absent when the board does not store everyone it ranks; data.board.worstAvailable and data.board.note say so. |

A board keeps its top 100 players, or the members that rank. A member it does not place has no row in data.best or data.worst; data.board.storedEntries counts the rows it keeps, and data.board.note says when the lowest rows are missing.

heDamage is enemy damage per HE, leaving out the HEs that only broke a smoke and hit no one; each row carries smokeBreaksLeftOut, how many it left out. smokeBreaks is those smoke breaks per 100 rounds. smokeBreakKills is a count, not a rate: the kills by the thrower and his teammates through a smoke his HE broke, within 3 seconds, ranked by the count, and each row carries ownKills, the kills he made himself. meta.definitions states each in full.

Request:

````
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://api.nades.ai/api/partner/v1/leaderboard?stat=flashKills&kind=team&window=3m&topTeams=10&limit=1"
````

MCP tools/call:

````
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "nades_leaderboard",
    "arguments": {
      "stat": "flashKills",
      "kind": "team",
      "window": "3m",
      "topTeams": 10,
      "limit": 1
    }
  }
}
````

Example answer:

````
{
  "data": {
    "stat": {
      "key": "flashKills",
      "label": "Flash kills per round",
      "unit": "per round",
      "decimals": 3,
      "siteBoard": true
    },
    "board": {
      "kind": "team",
      "window": "3m",
      "map": null,
      "topTeams": 10,
      "url": "https://nades.ai/leaderboards/example",
      "teamsInBand": 9,
      "ranked": 9,
      "unranked": {
        "belowGames": 0,
        "inactive": 0,
        "belowSample": 0
      },
      "floors": {
        "games": 20,
        "denominator": 500,
        "activeDays": 60
      },
      "poolAverage": 0.07,
      "storedEntries": 9,
      "worstAvailable": true
    },
    "best": [
      {
        "rank": 1,
        "name": "Example Team",
        "slug": "example-team",
        "url": "https://nades.ai/teams/example-team",
        "games": 52,
        "value": 0.081,
        "numerator": 210
      }
    ]
  }
}
````

## Stats for a group

GET `/v1/group`, MCP tool `nades_group`

One table of every utility stat for the top 10, 20 or 30, or for your own list of up to 30 teams or players.

Send exactly one of topTeams, teams or players; anything else is a 400.

| Name | Need | Allowed values | What it does |
| --- | --- | --- | --- |
| `topTeams` | one of | 10, 20 or 30 | The HLTV top teams. |
| `teams / players` | one of | up to 30, comma separated on REST, an array on MCP | Your own list, in place of a top band. A name that does not resolve is listed in data.unresolved. |
| `kind` | optional | team or player (default team) | Which of the two a top band holds. Only with topTeams; with teams or players it is a 400. |
| `stats` | optional | a comma separated list of flashKills, blindTime, heDamage, smokeBreaks, smokeBreakKills, molotovDamage, nadesPerRound, smokesPerRound or flashToKill, or all (default all) | The stats in the table. |
| `window` | optional | 1m, 3m, 6m, 1y or all (default 3m) | How far back games count. |
| `map` | optional | one map, as mirage or de_mirage | Reads every stat and the record on that map alone. |
| `sortBy` | optional | flashKills, blindTime, heDamage, smokeBreaks, smokeBreakKills, molotovDamage, nadesPerRound, smokesPerRound or flashToKill | Sorts the table by it. It must be one of the stats asked for, else a 400. Left out, the first of them. |
| `limit` | optional | 1 to 100 (default 50) | Rows on one page of a top band of players. Every other table comes whole. |
| `offset` | optional | 0 to 1000 (default 0) | Where the page starts, on a top band of players only. |

Every cell has a status: ranked, unranked (with its reason), not_on_board, not_stored or unresolved. A cell is never blank and never a zero. not_on_board means no game on that board in the window. not_stored means the figure is not known: the board keeps only its top 100 or its ranked members, or its list of those below the floors is not held, so the member may rank past them or sit below the floors. Any figures printed with it are the stored sums.

A band of players can run past one page. data.group carries total and truncated, and nextOffset when truncated; read on with offset set to it.

A team row carries a record. Where the record is not known it is null and the row has a note. compare does the same for the record of each side.

Request:

````
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://api.nades.ai/api/partner/v1/group?teams=example-team,other-team&stats=flashKills&window=3m"
````

MCP tools/call:

````
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "nades_group",
    "arguments": {
      "teams": [
        "example-team",
        "other-team"
      ],
      "stats": "flashKills",
      "window": "3m"
    }
  }
}
````

Example answer:

````
{
  "data": {
    "group": {
      "kind": "team",
      "requested": 2,
      "resolved": 2,
      "total": 2,
      "truncated": false
    },
    "window": "3m",
    "map": null,
    "columns": [
      {
        "key": "flashKills",
        "label": "Flash kills per round",
        "unit": "per round",
        "decimals": 3,
        "derived": false
      }
    ],
    "rows": [
      {
        "entity": {
          "name": "Example Team",
          "slug": "example-team",
          "url": "https://nades.ai/teams/example-team",
          "worldRank": 1,
          "team": null
        },
        "games": 52,
        "stats": {
          "flashKills": {
            "status": "ranked",
            "value": 0.081,
            "rank": 4,
            "of": 40,
            "groupRank": 1,
            "numerator": 210,
            "denominator": 2600
          }
        },
        "record": {
          "maps": 30,
          "mapsWon": 19,
          "mapWinPct": 0.633,
          "rounds": 640,
          "roundsWon": 340,
          "roundWinPct": 0.531,
          "sides": null,
          "reason": "coverage_below_floor",
          "coverage": 0.62,
          "refused": 9,
          "series": {
            "played": 12,
            "won": 8
          }
        }
      },
      {
        "entity": {
          "name": "Other Team",
          "slug": "other-team",
          "url": "https://nades.ai/teams/other-team",
          "worldRank": 9,
          "team": null
        },
        "games": 47,
        "stats": {
          "flashKills": {
            "status": "not_stored",
            "value": 0.066,
            "numerator": 150,
            "denominator": 2270
          }
        },
        "record": {
          "maps": 26,
          "mapsWon": 12,
          "mapWinPct": 0.462,
          "rounds": 560,
          "roundsWon": 268,
          "roundWinPct": 0.479,
          "sides": null,
          "reason": "coverage_below_floor",
          "coverage": 0.71,
          "refused": 7,
          "series": {
            "played": 10,
            "won": 4
          }
        }
      }
    ],
    "average": {
      "flashKills": {
        "value": 0.081,
        "numerator": 210,
        "denominator": 2600
      }
    },
    "best": {
      "flashKills": "example-team"
    },
    "worst": {
      "flashKills": "example-team"
    },
    "unresolved": [],
    "notes": [
      "not_stored: the release holds nothing that places this member on this board, since the board keeps only its top 100 or ranked members, or its list of those below the floors is not held. The member may rank past them or sit below the floors. Any figures shown are the stored sums."
    ]
  }
}
````

## List lineups

GET `/v1/lineups`, MCP tool `nades_lineups`

Lineups pros throw, for a team, a player or a map.

Send at least one of team, player or map; with none it is a 400. team and player together are a 400 too; map may narrow either.

| Name | Need | Allowed values | What it does |
| --- | --- | --- | --- |
| `team / player / map` | at least one | a slug or a name | Whose lineups, or which map. |
| `window` | optional | 3m or all (default 3m) | How far back games count. |
| `sort` | optional | best, throws, matches, win_rate, first_seen, trend, last_seen, longest_blind, most_kills, most_damage or rounds (default throws) | The order of the list. |
| `insta` | optional | include, exclude or only (default include) | Keeps instas in, leaves them out or lists them alone. |
| `family` | optional | smoke, flash, he or molotov | Keeps one grenade family. |
| `side` | optional | t or ct | Keeps one side. |
| `technique` | optional | standing_throw, crouch_throw, jump_throw, crouch_jump_throw, w_jump_throw, walk_jump_throw, run_jump_throw, w_throw, walk_throw, run_throw, crouch_walk_throw or airborne_throw | Keeps one way of throwing. |
| `from / to` | optional | a callout id, else a whole name or group, else part of one | The callout it is thrown from, and the one it lands on. Case and spaces are ignored. A value that matches no callout returns no rows, and data.note says so. |
| `minThrows / minRounds / minMaps` | optional | 0 to 1000000 | The least a row must hold. |
| `lastThrownAfter` | optional | a date like 2026-03-01 or an instant | Keeps lineups thrown at or after it. |
| `event` | optional | an edition id, a competition id or a name | What a team or player threw there. Needs team or player. |
| `limit` | optional | 1 to 30 (default 15) | Rows on one page. |
| `offset` | optional | the nextOffset of the previous answer (default 0) | Reads on from there. |

The floors and lastThrownAfter narrow the list before it is sorted and paged, so total, rank and nextOffset count the rows that are left. A lineup with no dated throw is left out by lastThrownAfter, and the note says how many.

technique, from and to read what the lineup states. A lineup that states none of what you ask cannot be placed and is left out; the note counts them. Before the next site rebuild they answer lineups null and a note.

event takes an edition id, a competition id or a name, as under /v1/event. It cannot be read with window, technique, from or to. Only the 30 lineups a team or player threw most there are stored, so sort and every filter read those 30, and meta.note says how many there were. sort is then one of throws, matches, rounds, win_rate or last_seen; the rows carry no first throw, trend or damage figures.

data.total counts the stored rows under the filters, and a lineup unpublished since the last build still counts until it reaches a page; rank counts the rows of each page in order. A page can hold fewer rows than limit, even none, while rows remain, so keep asking with offset set to data.nextOffset until it is null. rank at an offset you set yourself can jump across a removed row, and no row is lost.

Request:

````
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://api.nades.ai/api/partner/v1/lineups?team=example-team&sort=throws&limit=1"
````

MCP tools/call:

````
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "nades_lineups",
    "arguments": {
      "team": "example-team",
      "sort": "throws",
      "limit": 1
    }
  }
}
````

Example answer:

````
{
  "data": {
    "sort": "throws",
    "filters": {
      "map": null,
      "family": null,
      "side": null,
      "insta": "include",
      "technique": null,
      "from": null,
      "to": null,
      "minThrows": null,
      "minRounds": null,
      "minMaps": null,
      "lastThrownAfter": null
    },
    "scope": {
      "kind": "team",
      "name": "Example Team",
      "slug": "example-team",
      "url": "https://nades.ai/teams/example-team"
    },
    "total": 120,
    "lineups": [
      {
        "rank": 1,
        "lineupId": "example-lineup-id",
        "title": "Example Map A Site Smoke from Example Spot",
        "url": "https://nades.ai/example-map/smokes/example-lineup",
        "family": "smoke",
        "side": "t",
        "technique": "jumpthrow",
        "from": "Example Spot",
        "to": "A Site",
        "insta": false,
        "usage": {
          "...": "usage figures, cut from this example"
        },
        "video": {
          "url": "https://assets.nades.ai/example.mp4"
        },
        "image": {
          "release": "https://assets.nades.ai/example.jpg"
        },
        "map": "example-map",
        "throws": 40,
        "matchMaps": 12,
        "rounds": 30,
        "wonRounds": 17,
        "winPct": 0.567,
        "allTimeThrows": 90,
        "firstThrownAt": "2025-09-01T12:00:00.000Z",
        "lastThrownAt": "2026-01-01T12:00:00.000Z",
        "recentThrows": 30,
        "priorThrows": 10
      }
    ],
    "nextOffset": 1,
    "note": "`total` counts the stored rows under these filters; a lineup unpublished since the last build still counts until it reaches a page, so a page can hold fewer rows than `limit`, even none, while rows remain. Read on with `offset` set to `nextOffset`, until it is null; `rank` counts stored rows, and under `best` a row can come twice, but none is lost."
  }
}
````

## One lineup

GET `/v1/lineup`, MCP tool `nades_lineup`

One lineup by id or nades.ai URL: video, how to throw it, how often pros throw it and who throws it.

| Name | Need | Allowed values | What it does |
| --- | --- | --- | --- |
| `lineup` | required | an id or a nades.ai URL | The lineup. |
| `window` | optional | 1m, 3m, 6m, 1y or all | Reads who throws it, teams and players, over that window. It has no default: left out, the answer's window is null and data.population holds the head of the lineup's page. The tournaments in data.population stay that head either way. |

The teams and players that throw it carry a slug and a url; url is null where the site has no page for them.

Request:

````
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://api.nades.ai/api/partner/v1/lineup?lineup=example-lineup-id&window=3m"
````

MCP tools/call:

````
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "nades_lineup",
    "arguments": {
      "lineup": "example-lineup-id",
      "window": "3m"
    }
  }
}
````

Example answer:

````
{
  "data": {
    "lineupId": "example-lineup-id",
    "title": "Example Map A Site Smoke from Example Spot",
    "url": "https://nades.ai/example-map/smokes/example-lineup",
    "family": "smoke",
    "side": "t",
    "technique": "jumpthrow",
    "from": "Example Spot",
    "to": "A Site",
    "insta": false,
    "usage": {
      "...": "usage figures, cut from this example"
    },
    "video": {
      "url": "https://assets.nades.ai/example.mp4"
    },
    "image": {
      "release": "https://assets.nades.ai/example.jpg"
    },
    "setup": {
      "...": "stand spot, aim and throw, cut from this example"
    },
    "evidence": {
      "...": "evidence, cut from this example"
    },
    "commonness": {
      "...": "commonness, cut from this example"
    },
    "effectStanding": {
      "...": "effect standing, cut from this example"
    },
    "thrownWithSpots": [],
    "population": {
      "teams": {
        "entities": [
          {
            "id": "example-team-id",
            "name": "Example Team",
            "slug": "example-team",
            "url": "https://nades.ai/teams/example-team",
            "throws": 40,
            "lastSeenAt": "2026-01-01T12:00:00.000Z"
          }
        ],
        "total": 14,
        "overflow": 13,
        "unattributedThrows": 0
      },
      "players": {
        "entities": [
          {
            "id": "example-player-id",
            "name": "examplePlayer",
            "slug": "exampleplayer",
            "url": "https://nades.ai/players/exampleplayer",
            "throws": 25,
            "lastSeenAt": "2026-01-01T12:00:00.000Z"
          }
        ],
        "total": 40,
        "overflow": 39,
        "unattributedThrows": 0
      },
      "tournaments": {
        "entities": [
          {
            "id": "e1",
            "name": "Example Cup",
            "throws": 60,
            "lastSeenAt": "2026-01-01T12:00:00.000Z"
          }
        ],
        "total": 3,
        "overflow": 2,
        "unattributedThrows": 0
      }
    }
  }
}
````

## Trending lineups

GET `/v1/trending`, MCP tool `nades_trending`

Lineups rising, new and fading: the last 30 days against the 30 before.

| Name | Need | Allowed values | What it does |
| --- | --- | --- | --- |
| `map` | optional | one map, as mirage or de_mirage | Keeps the lineups of that map. |
| `family` | optional | smoke, flash, he or molotov | Keeps one grenade family. |
| `limit` | optional | 1 to 30 (default 10) | Rows in each of the three lists. |

A filter runs over the three lists the site stores, each its top 60, so a filtered list can be shorter than limit.

Request:

````
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://api.nades.ai/api/partner/v1/trending?map=example-map&limit=1"
````

MCP tools/call:

````
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "nades_trending",
    "arguments": {
      "map": "example-map",
      "limit": 1
    }
  }
}
````

Example answer:

````
{
  "data": {
    "generatedAt": "2026-01-01T06:00:00.000Z",
    "summary": {
      "totalThrows": 120000,
      "recentThrows": 9000,
      "previousThrows": 8000,
      "recentRounds": 40000,
      "previousRounds": 38000,
      "activeLineups": 900,
      "recentTournaments": 6,
      "newLineups": 14
    },
    "rising": [
      {
        "rank": 1,
        "lineupId": "example-lineup-id",
        "title": "Example Map A Site Smoke from Example Spot",
        "url": "https://nades.ai/example-map/smokes/example-lineup",
        "family": "smoke",
        "side": "t",
        "technique": "jumpthrow",
        "from": "Example Spot",
        "to": "A Site",
        "insta": false,
        "usage": {
          "...": "usage figures, cut from this example"
        },
        "video": {
          "url": "https://assets.nades.ai/example.mp4"
        },
        "image": {
          "release": "https://assets.nades.ai/example.jpg"
        },
        "delta": {
          "recent": 30,
          "previous": 10,
          "percent": 200,
          "recentRounds": 25,
          "previousRounds": 9,
          "recentTeams": [
            {
              "id": "t1",
              "name": "Example Team",
              "slug": "example-team",
              "url": "https://nades.ai/teams/example-team"
            }
          ],
          "recentTournaments": [
            {
              "id": "e1",
              "name": "Example Cup"
            }
          ],
          "lastSeenAt": "2026-01-01T12:00:00.000Z"
        }
      }
    ],
    "new": [],
    "fading": [],
    "note": "Example note."
  }
}
````

## List matches

GET `/v1/matches`, MCP tool `nades_matches`

Pro series, newest first.

Send at most one of team, player or topTeams; more is a 400.

| Name | Need | Allowed values | What it does |
| --- | --- | --- | --- |
| `event` | optional | an edition id, a competition id or a name | Keeps the series of a tournament. The notes say how it reads. |
| `team / player` | optional | a slug or a name | Keeps the series of that team or player. |
| `map` | optional | one map, as mirage or de_mirage | Keeps series with that map published on the site, so a series played on it with no published lineup is left out. |
| `q` | optional | a word | Finds team and event names. The notes say how it reads. |
| `from / to` | optional | a date like 2026-03-01, or an instant like 2026-03-01T12:00:00Z | Keeps series played from one, up to the other. |
| `vs` | optional | an opponent, a slug or a name | Keeps series against it. Needs team, and another team than team. |
| `topTeams` | optional | 10, 20 or 30 | Keeps series with a team of the HLTV top teams on either side. |
| `limit` | optional | 1 to 50 (default 20) | Rows on one page. |
| `cursor` | optional | from the previous answer | Reads the next page. |

from and to are both inclusive. A date is the whole UTC day, so to=2026-03-01 keeps what was played on that day; an instant is that moment. from after to is a 400.

event takes an edition id, a competition id or a name. An edition id, or a name that reaches one edition, lists that tournament. A competition id, or a name that reaches several of its editions, lists all of them, and meta.event and meta.note say so. A name that fits different competitions is a 409. Each row carries event with id, editionId and name; so do match and recent.

A cursor is bound to the filters it was given under. A cursor from a list with other filters is a 400 BAD_REQUEST. Page with the same parameters and the cursor from the last answer.

q finds team names and event names, an event by its latest name. It does not find a side with no team page, an older edition's name or text spanning two names. Past 32 teams or events it takes the closest 32 teams or the newest 32 events, and data.note says so.

q with topTeams is a 400 on q. So is q with map, unless team, player or event is sent too; then q and map both narrow that one list.

A cursor that this API did not give out is a 400 BAD_REQUEST.

Request:

````
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://api.nades.ai/api/partner/v1/matches?team=example-team&limit=1"
````

MCP tools/call:

````
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "nades_matches",
    "arguments": {
      "team": "example-team",
      "limit": 1
    }
  }
}
````

Example answer:

````
{
  "data": {
    "matches": [
      {
        "seriesId": "example-series",
        "playedAt": "2026-01-01T12:00:00.000Z",
        "tournament": "Example Cup 2026",
        "event": {
          "id": "example-competition",
          "editionId": "example-edition",
          "name": "Example Cup 2026"
        },
        "format": "bo3",
        "url": "https://nades.ai/matches/example-series",
        "matchUrl": null,
        "teamA": {
          "name": "Example Team",
          "slug": "example-team",
          "url": "https://nades.ai/teams/example-team",
          "logoUrl": "https://assets.nades.ai/example-logo.png",
          "worldRank": 1
        },
        "teamB": {
          "name": "Other Team",
          "slug": "other-team",
          "url": "https://nades.ai/teams/other-team",
          "logoUrl": null,
          "worldRank": 9
        },
        "score": {
          "a": 2,
          "b": 0
        },
        "winner": "a",
        "maps": [
          {
            "map": "example-map",
            "score": {
              "a": 13,
              "b": 7
            },
            "winner": "a",
            "roundsPlayedBySide": null,
            "publishedLineups": 12
          }
        ]
      }
    ],
    "nextCursor": null
  }
}
````

## One match

GET `/v1/match`, MCP tool `nades_match`

One series in full by id or nades.ai URL.

| Name | Need | Allowed values | What it does |
| --- | --- | --- | --- |
| `match` | required | an id or a nades.ai URL | The series. |
| `lineups` | optional | 0 to 20 (default 8) | Lineups in the answer, most thrown first. |

Request:

````
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://api.nades.ai/api/partner/v1/match?match=example-series&lineups=1"
````

MCP tools/call:

````
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "nades_match",
    "arguments": {
      "match": "example-series",
      "lineups": 1
    }
  }
}
````

Example answer:

````
{
  "data": {
    "seriesId": "example-series",
    "playedAt": "2026-01-01T12:00:00.000Z",
    "tournament": "Example Cup 2026",
    "event": {
      "id": "example-competition",
      "editionId": "example-edition",
      "name": "Example Cup 2026"
    },
    "format": "bo3",
    "url": "https://nades.ai/matches/example-series",
    "combos": [
      {
        "map": "example-map",
        "teams": [
          {
            "team": {
              "name": "Example Team",
              "slug": "example-team",
              "url": "https://nades.ai/teams/example-team"
            },
            "uses": [
              {
                "combo": {
                  "label": "Example combo"
                },
                "rounds": 6
              }
            ]
          }
        ]
      }
    ],
    "lineupsThrown": [
      {
        "lineupId": "example-lineup-id",
        "title": "Example Map A Site Smoke from Example Spot",
        "url": "https://nades.ai/example-map/smokes/example-lineup",
        "family": "smoke",
        "side": "t",
        "technique": "jumpthrow",
        "from": "Example Spot",
        "to": "A Site",
        "insta": false,
        "usage": {
          "...": "usage figures, cut from this example"
        },
        "video": {
          "url": "https://assets.nades.ai/example.mp4"
        },
        "image": {
          "release": "https://assets.nades.ai/example.jpg"
        },
        "throws": 4,
        "matchMaps": 1,
        "teamThrows": [
          {
            "team": {
              "name": "Example Team",
              "slug": "example-team",
              "url": "https://nades.ai/teams/example-team"
            },
            "throws": 4,
            "sides": {
              "...": "sides, cut from this example"
            }
          }
        ]
      }
    ],
    "note": "Example note."
  }
}
````

## A team's latest series

GET `/v1/recent`, MCP tool `nades_recent_matches`

A team's latest series.

| Name | Need | Allowed values | What it does |
| --- | --- | --- | --- |
| `team` | required | a slug or a name | The team. |
| `matches` | optional | 1 to 5 (default 3) | How many series. |

Request:

````
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://api.nades.ai/api/partner/v1/recent?team=example-team&matches=1"
````

MCP tools/call:

````
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "nades_recent_matches",
    "arguments": {
      "team": "example-team",
      "matches": 1
    }
  }
}
````

Example answer:

````
{
  "data": {
    "team": {
      "name": "Example Team",
      "slug": "example-team",
      "url": "https://nades.ai/teams/example-team"
    },
    "matches": [
      {
        "seriesId": "example-series",
        "playedAt": "2026-01-01T12:00:00.000Z",
        "tournament": "Example Cup 2026",
        "event": {
          "id": "example-competition",
          "editionId": "example-edition",
          "name": "Example Cup 2026"
        },
        "format": "bo3",
        "url": "https://nades.ai/matches/example-series",
        "opponent": {
          "name": "Other Team",
          "slug": "other-team",
          "url": "https://nades.ai/teams/other-team"
        },
        "score": {
          "team": 2,
          "opponent": 0
        },
        "won": true,
        "players": {
          "team": [],
          "opponent": []
        },
        "maps": [
          {
            "map": "example-map",
            "score": {
              "team": 13,
              "opponent": 7
            },
            "won": true,
            "sides": {
              "t": {
                "won": 6,
                "played": 12
              },
              "ct": {
                "won": 7,
                "played": 8
              }
            }
          }
        ]
      }
    ],
    "note": "Series score counts maps won, or rounds at bo1. Use nades_match for one series' combos and lineups."
  }
}
````

## A team's combos

GET `/v1/combos`, MCP tool `nades_team_combos`

The combos a team runs.

| Name | Need | Allowed values | What it does |
| --- | --- | --- | --- |
| `team` | required | a slug or a name | The team. |
| `map` | optional | one map, as mirage or de_mirage | Keeps the combos of that map. |
| `window` | optional | 1m, 3m, 6m, 1y or all (default 3m) | How far back the team's own rounds count. allPros stays every pro team's whole record. |
| `insta` | optional | include, exclude or only (default include) | Keeps every combo, leaves out those with an insta in them or lists them alone. |
| `limit` | optional | 1 to 20 (default 10) | Rows in the answer. |

Only combos the team ran in at least three rounds, as the team's combos page lists them.

Request:

````
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://api.nades.ai/api/partner/v1/combos?team=example-team&window=3m&limit=1"
````

MCP tools/call:

````
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "nades_team_combos",
    "arguments": {
      "team": "example-team",
      "window": "3m",
      "limit": 1
    }
  }
}
````

Example answer:

````
{
  "data": {
    "total": 12,
    "scoped": {
      "window": "3m",
      "coverage": {
        "...": "coverage, cut from this example"
      }
    },
    "combos": [
      {
        "label": "Example combo",
        "team": {
          "rounds": 14,
          "wonRounds": 9,
          "decidedRounds": 14,
          "winPct": 0.643,
          "lastUsedAt": "2026-01-01T12:00:00.000Z"
        },
        "allPros": {
          "rounds": 80,
          "teams": 9,
          "winShare": 0.55
        },
        "breakdown": {
          "...": "breakdown, cut from this example"
        },
        "seats": [],
        "steps": []
      }
    ],
    "note": "Example note."
  }
}
````

## One combo

GET `/v1/combo`, MCP tool `nades_combo`

One combo on a map by its slug: throws, record and how the rounds run after it.

| Name | Need | Allowed values | What it does |
| --- | --- | --- | --- |
| `map, combo` | required | a map, and the combo's slug | The combo. |
| `side` | optional | t or ct | Breakdown only: keeps one side. |
| `plan` | optional | a, b or none | Breakdown only: keeps one plan. |
| `window` | optional | 1m, 3m, 6m, 1y or all (default 3m) | How far back rounds count, in the figures and the breakdown. The notes say what cannot follow it. |
| `team, vs` | optional | a team, and an opponent | Breakdown only: keeps the rounds of that team, against that opponent. |
| `buy` | optional | all, full, force, eco or pistol (default all) | Breakdown only: keeps rounds of one buy. |
| `top` | optional | 10, 20 or 30 | Breakdown only: keeps the HLTV top teams. |
| `won` | optional | true or false (default false) | Breakdown only: true keeps only the rounds won. |

side, plan, team, vs, buy, top and won reach only data.breakdown: rounds, teams, winShare and steps do not move with them.

rounds, teams, winShare and lastSeenAt follow window, and scoped.window says which window they are for: all on a release built before windows. The combos in linked cannot follow it, so each is counted under linkedWindow, and linkedNote says so.

Request:

````
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://api.nades.ai/api/partner/v1/combo?map=example-map&combo=example-combo&window=3m"
````

MCP tools/call:

````
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "nades_combo",
    "arguments": {
      "map": "example-map",
      "combo": "example-combo",
      "window": "3m"
    }
  }
}
````

Example answer:

````
{
  "data": {
    "comboId": "example-combo-id",
    "title": "Example combo",
    "url": "https://nades.ai/example-map/combos/example-combo",
    "side": "t",
    "kind": "team",
    "map": "example-map",
    "label": "Example combo",
    "standing": "listed",
    "scoped": {
      "window": "3m"
    },
    "rounds": 80,
    "teams": 9,
    "winShare": 0.55,
    "lastSeenAt": "2026-01-01T12:00:00.000Z",
    "allTime": {
      "rounds": 200,
      "teams": 12,
      "winShare": 0.53
    },
    "blurb": "Example blurb.",
    "steps": [],
    "linked": [
      {
        "comboId": "example-linked-id",
        "title": "Example linked combo",
        "url": "https://nades.ai/example-map/combos/example-linked",
        "side": "t",
        "kind": "team",
        "rounds": 55,
        "winShare": 0.5
      }
    ],
    "linkedWindow": "3m",
    "linkedNote": "A linked combo's `rounds` and `winShare` are counted under `linkedWindow`, whatever `window` asks. Ask nades_combo for one to read it under your window.",
    "breakdown": null
  }
}
````

## List events

GET `/v1/events`, MCP tool `nades_events`

Every event, with its series count and last play.

Takes no parameters.

An event is a competition. Each row carries editions, the tournaments it held, newest first, each with editionId, name, firstPlayedAt, lastPlayedAt and series. editions is null where the release does not hold them yet, and data.note says so. Pass an editionId to event or matches to ask about one tournament.

Request:

````
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://api.nades.ai/api/partner/v1/events"
````

MCP tools/call:

````
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "nades_events",
    "arguments": {}
  }
}
````

Example answer:

````
{
  "data": {
    "events": [
      {
        "id": "example-competition",
        "name": "Example Cup",
        "logoUrl": null,
        "series": 24,
        "lastPlayedAt": "2026-01-01T12:00:00.000Z",
        "editions": [
          {
            "editionId": "example-edition",
            "name": "Example Cup 2026",
            "firstPlayedAt": "2026-01-01T09:00:00.000Z",
            "lastPlayedAt": "2026-01-01T12:00:00.000Z",
            "series": 24
          }
        ]
      }
    ]
  }
}
````

## One tournament

GET `/v1/event`, MCP tool `nades_event`

What a team or player did at one tournament, or how the whole tournament went.

| Name | Need | Allowed values | What it does |
| --- | --- | --- | --- |
| `event` | required | an edition id, a competition id or a name | The tournament. find with kind event lists them. |
| `team / player` | optional | a slug or a name; call find when unsure | What one team or player did there. One of them, not both. |
| `map` | optional | one map, as mirage or de_mirage | Reads the figures, the places and the table on that map, and keeps the series that played it. A team's or player's figures on each map stay in scopes. |
| `kind` | optional | teams or players (default both) | Which of the tables. The table only. |
| `sort` | optional | thrown, smokes, flashes, hes, molotovs, games, rounds, heDamageTotal, molotovDamageTotal, blindSeconds, flashesLedToKill, heSmokeBreaks, smokeBreakOwnKills, flashKills, blindTime, heDamage, smokeBreaks, smokeBreakKills, molotovDamage, nadesPerRound, smokesPerRound, flashToKill (default thrown); for kind teams also series, seriesWon, mapsWon, roundsWon | The order of the table. The table only. |
| `limit` | optional | 1 to 100 (default 30) | Rows in the table. The table only. |
| `offset` | optional | 0 to 1000 (default 0) | Where the table starts. The table only. |

Three shapes. With neither team nor player: data.event, then data.teams and data.players ranked by sort, with total and a nextOffset for each. With team: its figures, its place on every stat, its figures on each map, its roster at the tournament and its series there. With player: the same for one player, with their team. Each figure is counted as the leaderboards count it, over the maps nades.ai analysed at that edition.

A place is rank among the rows with a figure, highest first, with of for how many have one. Equal values share a place. No games floor applies, so read games beside a rate. sort on a team stat needs kind teams, and series and seriesWon do not read beside map. kind, sort, limit and offset are refused beside team or player.

event resolves in this order: an edition id, a competition id, then a name, matched whole and then as part of an edition name. A competition id, or a name that fits several editions of one competition, answers for the newest edition; meta.event names it and meta.note says why and lists the others. Send an edition id or its full name to pin one. A name that fits different competitions is a 409 AMBIGUOUS_NAME with data.candidates; a name that fits none is a 404.

A team or player with no game at the tournament is a 404. When the word chose the newest of several editions, the message names it and up to 3 others, and asks you to ask by edition. Event rows carry flashToKillPartial where flashToKill was measured on only some games. flashesLedToKill and flashToKill can be null, and hold no rank when not measured or partial. mapsWon and roundsWon are null where a map belongs to a series that names no two teams, and data.note says so. A tournament too large to keep per map is held over all maps only: ask with map and the figures are null with a note. The series list holds the 20 latest; matches with event set to the editionId pages through the rest.

Request:

````
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://api.nades.ai/api/partner/v1/event?event=Example%20Cup%202026&player=example-player"
````

MCP tools/call:

````
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "nades_event",
    "arguments": {
      "event": "Example Cup 2026",
      "player": "example-player"
    }
  }
}
````

Example answer:

````
{
  "data": {
    "event": {
      "id": "example-competition",
      "editionId": "example-edition",
      "name": "Example Cup 2026",
      "competitionName": "Example Cup",
      "firstPlayedAt": "2026-01-01T09:00:00.000Z",
      "lastPlayedAt": "2026-01-01T12:00:00.000Z",
      "series": 24,
      "maps": 40,
      "analysedMaps": 38,
      "mapScopesDropped": false
    },
    "map": null,
    "player": {
      "id": "example-player-id",
      "name": "Example Player",
      "slug": "example-player",
      "url": "https://nades.ai/players/example-player",
      "team": {
        "name": "Example Team",
        "slug": "example-team",
        "url": "https://nades.ai/teams/example-team"
      },
      "figures": {
        "games": 9,
        "rounds": 210,
        "thrown": 640,
        "flashKills": 0.081,
        "...": "every other stat of the row, cut from this example"
      },
      "ranks": {
        "thrown": {
          "rank": 3,
          "of": 80
        },
        "flashKills": {
          "rank": 1,
          "of": 80
        },
        "...": "a place on every other stat, cut from this example"
      },
      "scopes": [
        {
          "map": null,
          "games": 9,
          "thrown": 640,
          "...": "cut"
        },
        {
          "map": "example-map",
          "games": 3,
          "thrown": 220,
          "...": "cut"
        }
      ]
    },
    "matches": [
      {
        "seriesId": "example-series",
        "playedAt": "2026-01-01T12:00:00.000Z",
        "...": "the same row as the matches query, cut from this example"
      }
    ]
  }
}
````

## One map

GET `/v1/map`, MCP tool `nades_map`

One map: the lineups pros throw most in the last three months, when in the round, and its top combos.

| Name | Need | Allowed values | What it does |
| --- | --- | --- | --- |
| `map` | required | a slug or a name | The map. |
| `family` | optional | smoke, flash, he or molotov | Keeps the lineups and combos of one grenade family. |
| `side` | optional | t or ct | Picks the lineups and combos of one side. Each lineup's figures stay over all sides. |
| `insta` | optional | include, exclude or only (default include) | Keeps instas in, leaves them out or lists them alone, in the lineups and in the combos that hold one. |
| `lineups` | optional | 0 to 30 (default 15) | Lineups in the answer. |
| `combos` | optional | 0 to 20 (default 10) | Combos in the answer. |

The answer always reads the last three months; this query takes no window.

Request:

````
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://api.nades.ai/api/partner/v1/map?map=example-map&lineups=1&combos=1"
````

MCP tools/call:

````
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "nades_map",
    "arguments": {
      "map": "example-map",
      "lineups": 1,
      "combos": 1
    }
  }
}
````

Example answer:

````
{
  "data": {
    "map": "example-map",
    "coverage": {
      "matchMaps3m": 120,
      "throws3m": 9000,
      "windowFrom": "2025-10-01T00:00:00Z"
    },
    "lineups": [
      {
        "lineupId": "example-lineup-id",
        "title": "Example Map A Site Smoke from Example Spot",
        "url": "https://nades.ai/example-map/smokes/example-lineup",
        "family": "smoke",
        "side": "t",
        "technique": "jumpthrow",
        "from": "Example Spot",
        "to": "A Site",
        "insta": false,
        "usage": {
          "...": "usage figures, cut from this example"
        },
        "video": {
          "url": "https://assets.nades.ai/example.mp4"
        },
        "image": {
          "release": "https://assets.nades.ai/example.jpg"
        },
        "throws": 300,
        "rounds": 250,
        "teams": 14,
        "shareOfRounds": 0.04
      }
    ],
    "lineupsNote": "Example note.",
    "combos": [
      {
        "label": "Example combo",
        "rounds": 80,
        "teams": 9,
        "winShare": 0.55,
        "steps": []
      }
    ],
    "hubs": {
      "smokes": "https://nades.ai/example-map/smokes"
    }
  }
}
````

## Team tendencies on a map

GET `/v1/tendencies`, MCP tool `nades_tendencies`

How often a team does each thing on a map.

| Name | Need | Allowed values | What it does |
| --- | --- | --- | --- |
| `team, map` | required | a slug or a name | The team, and the map. |
| `window` | optional | 1m, 3m, 6m, 1y or all (default 3m) | How far back rounds count. |
| `side` | optional | both, t or ct (default both) | Keeps one side. |
| `buy` | optional | all, full, force, eco or pistol (default all) | Keeps rounds of one buy. |

It reads each team's newest 1,200 rounds on a map, and a fact needs 8 rounds. Where nades.ai holds no fact for the team here, data.note says so, and rounds is 0 when it holds no rounds either.

Request:

````
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://api.nades.ai/api/partner/v1/tendencies?team=example-team&map=example-map&window=3m"
````

MCP tools/call:

````
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "nades_tendencies",
    "arguments": {
      "team": "example-team",
      "map": "example-map",
      "window": "3m"
    }
  }
}
````

Example answer:

````
{
  "data": {
    "team": {
      "name": "Example Team",
      "slug": "example-team",
      "url": "https://nades.ai/teams/example-team"
    },
    "map": "example-map",
    "rounds": 96,
    "roster": [
      "examplePlayer"
    ],
    "builtAt": "2026-01-01T06:00:00.000Z",
    "players": [
      {
        "name": "examplePlayer",
        "slug": "exampleplayer",
        "url": "https://nades.ai/players/exampleplayer"
      }
    ],
    "ct": [],
    "t": [
      {
        "...": "a tendency fact: what the team does, its rate and its rounds, cut from this example"
      }
    ],
    "both": [],
    "note": "Example note."
  }
}
````

Terms as agreed with nades.ai. Questions: support@nades.ai
