/api/v1/botsno key neededList published bots
- page
- query
- limit
- query
- sort
- query
- search
- query
200 · 422 · 429
Read bot data, read the votes for your own bot, and report your server count. REST over HTTPS, JSON both ways.
The full specification as OpenAPI 3.1: /api/v1/openapi.json
Create keys in your bot dashboard; you see them exactly once. They belong in an environment variable, not in your source. The two read endpoints also work without a key, at a lower limit.
const response = await fetch('https://topbot.gg/api/v1/bots/155149108183695360', {
headers: { Authorization: `Bearer ${process.env.TOPBOT_TOKEN}` },
});
if (!response.ok) {
const { error } = await response.json();
throw new Error(`${error.code}: ${error.message}`);
}
const { data } = await response.json();
console.log(data.name, data.votesTotal);Counted per minute and key, or per minute and IP when anonymous. Every response carries the numbers; a 429 adds a Retry-After header.
| Tier | Requests per minute |
|---|---|
| Without a key | 20 |
| Tier 1 — standard | 60 |
| Tier 2 — erhöht | 240 |
| Tier 3 — partner | 1200 |
x-ratelimit-limitx-ratelimit-remainingx-ratelimit-reset/api/v1/botsno key neededList published bots
200 · 422 · 429
/api/v1/bots/{id}no key neededGet one bot
200 · 404 · 429
/api/v1/bots/{id}/votesRecent votes for your bot
Requires the `votes:read` scope. The key must belong to this bot.
200 · 401 · 403 · 429
/api/v1/bots/{id}/check/{userId}Has this user voted?
Requires the `votes:read` scope. Use this before handing out a reward. Returns 200 with `voted: false` when they have not — that is an answer, not an error.
200 · 401 · 403 · 429
/api/v1/bots/{id}/statsReport your server count
Requires the `stats:write` scope. Call this when your shard counts change — once every few minutes is plenty.
200 · 401 · 403 · 422 · 429
We call your URL as soon as someone votes for your bot. Verify the signature before handing out a reward — otherwise anyone who knows your URL can reward themselves.
import { createHmac, timingSafeEqual } from 'node:crypto';
app.post('/topbot', express.raw({ type: 'application/json' }), (request, response) => {
const timestamp = request.header('x-topbot-timestamp');
const signature = request.header('x-topbot-signature');
const body = request.body.toString('utf8');
// Wiederholte alte Aufrufe abwehren.
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) {
return response.sendStatus(400);
}
const expected = createHmac('sha256', process.env.TOPBOT_WEBHOOK_SECRET)
.update(`${timestamp}.${body}`)
.digest('hex');
// In konstanter Zeit vergleichen.
if (
signature.length !== expected.length ||
!timingSafeEqual(Buffer.from(signature), Buffer.from(expected))
) {
return response.sendStatus(401);
}
const payload = JSON.parse(body);
if (payload.event === 'vote' && !payload.flagged) {
grantReward(payload.user);
}
response.sendStatus(200);
});Answer with any 2xx. On 5xx and 429 we retry six times with growing delays (10s, 40s, 90s, 160s, 250s); on other 4xx we do not retry. After 20 finally failed deliveries we switch the webhook off and let you know.
An SVG you embed in your README or on your site. It follows the viewer’s system setting and is cached for five minutes. Use theme=dark or theme=light to pin one version, compact=1 to drop the label.
[](https://topbot.gg/en/bots/YOUR-SLUG)