Zum Inhalt springen

API v1

Bot-Daten lesen, die Stimmen für deinen eigenen Bot abfragen und deinen Server-Count melden. REST über HTTPS, JSON hin und zurück.

Die vollständige Spezifikation als OpenAPI 3.1: /api/v1/openapi.json

Anmeldung

Schlüssel legst du im Dashboard deines Bots an und siehst sie genau einmal. Sie gehören in eine Umgebungsvariable, nicht in den Quelltext. Die beiden Lese-Endpunkte funktionieren auch ohne Schlüssel, dann aber mit einem knapperen 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);

Limits

Gezählt wird je Minute und Schlüssel, anonym je Minute und IP. Jede Antwort trägt die Zahlen mit; ein 429 zusätzlich einen Retry-After-Header.

StufeAnfragen pro Minute
Ohne Schlüssel20
Stufe 1 — standard60
Stufe 2 — erhöht240
Stufe 3 — partner1200
  • x-ratelimit-limit
  • x-ratelimit-remaining
  • x-ratelimit-reset

Endpunkte

GET/api/v1/botsohne Schlüssel

List published bots

page
query
limit
query
sort
query
search
query

200 · 422 · 429

GET/api/v1/bots/{id}ohne Schlüssel

Get one bot

id
path

200 · 404 · 429

GET/api/v1/bots/{id}/votes

Recent votes for your bot

Requires the `votes:read` scope. The key must belong to this bot.

id
path
limit
query
hours
query — Only votes from the last n hours (max 720).

200 · 401 · 403 · 429

GET/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.

id
path
userId
path — Discord user ID.

200 · 401 · 403 · 429

POST/api/v1/bots/{id}/stats

Report your server count

Requires the `stats:write` scope. Call this when your shard counts change — once every few minutes is plenty.

id
path

200 · 401 · 403 · 422 · 429

Vote-Webhooks

Wir rufen deine Adresse auf, sobald jemand für deinen Bot gestimmt hat. Prüfe die Signatur, bevor du eine Belohnung ausschüttest — sonst kann jeder, der deine Adresse kennt, sich selbst beschenken.

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);
});

Antworte mit einem beliebigen 2xx. Bei 5xx und 429 wiederholen wir sechsmal mit wachsendem Abstand (10 s, 40 s, 90 s, 160 s, 250 s), bei anderen 4xx gar nicht. Nach 20 endgültig gescheiterten Zustellungen schalten wir den Webhook ab und sagen dir Bescheid.

Badges

Ein SVG, das du in deine README oder auf deine Seite einbindest. Es folgt der Systemeinstellung des Betrachters und wird fünf Minuten lang gecacht. Mit theme=dark oder theme=light legst du eine Fassung fest, mit compact=1 fällt die Beschriftung weg.

votesserversrating
[![Topbot.gg](https://topbot.gg/api/badge/155149108183695360?variant=votes)](https://topbot.gg/de/bots/YOUR-SLUG)