> ## Documentation Index
> Fetch the complete documentation index at: https://docs.discordbotlist.lol/llms.txt
> Use this file to discover all available pages before exploring further.

# Discord Bot List API

> Post your server count, publish your commands, and check whether somebody voted — in about five minutes.

Five endpoints, one token. Everything here is HTTP and JSON, and the field names
deliberately match what the other bot lists use, so a library that already posts
to several of them can add this one as configuration rather than as code.

There is also an [official client for JavaScript and Python](/guides/libraries)
if you would rather not write the autoposter, the retry and the webhook signature
check yourself.

<CardGroup cols={2}>
  <Card title="Post your server count" icon="server" href="/api-reference/bot/post-the-server-count">
    One POST on a timer. The number appears on your page and in every listing row.
  </Card>

  <Card title="Check a vote" icon="check" href="/api-reference/votes/check-a-user-vote">
    The endpoint behind vote-for-perks. 600 requests a minute, per bot.
  </Card>

  <Card title="Receive vote webhooks" icon="webhook" href="/guides/vote-webhook">
    We POST to your URL when somebody votes. Signed, and retried five times.
  </Card>

  <Card title="Publish your commands" icon="terminal" href="/api-reference/bot/publish-the-commands">
    Forward what you registered with Discord. Your page stops going stale.
  </Card>
</CardGroup>

## Get a token

<Steps>
  <Step title="List your bot">
    [Submit it](https://discordbotlist.lol/submit) with its Discord application
    id. Approval is manual and usually same-day.
  </Step>

  <Step title="Open its edit page">
    `https://discordbotlist.lol/bot/<your-bot-id>/edit`, under **API token**.
  </Step>

  <Step title="Generate the token">
    It is shown **once**. Store it the way you store your Discord token — in the
    environment, not in the repository. Losing it means generating a new one,
    which invalidates the old immediately.
  </Step>
</Steps>

<Warning>
  A token acts on exactly one listing. It cannot read or write another bot, and
  asking about one answers `404` rather than `403` — so a leaked token cannot be
  used to find out which other bots exist.
</Warning>

## Your first request

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://discordbotlist.lol/api/v1/bots/YOUR_BOT_ID/stats \
    -H "Authorization: YOUR_API_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{"server_count": 1482}'
  ```

  ```js Node theme={null}
  await fetch(`https://discordbotlist.lol/api/v1/bots/${botId}/stats`, {
    method: 'POST',
    headers: {
      Authorization: process.env.DBL_TOKEN,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ server_count: client.guilds.cache.size }),
  });
  ```

  ```python Python theme={null}
  import os, requests

  requests.post(
      f"https://discordbotlist.lol/api/v1/bots/{bot_id}/stats",
      headers={"Authorization": os.environ["DBL_TOKEN"]},
      json={"server_count": len(bot.guilds)},
  )
  ```
</CodeGroup>

```json Response theme={null}
{ "success": true, "server_count": 1482 }
```

## What each endpoint costs you

Rate limits are per token, per minute, and they are sized for what the endpoint
is actually for rather than set to one number for tidiness.

| Endpoint    | Method | Limit   | Why that number                                               |
| ----------- | ------ | ------- | ------------------------------------------------------------- |
| `/stats`    | `POST` | 60/min  | A sharded bot posts once per shard.                           |
| `/commands` | `PUT`  | 10/min  | Published on startup, so it only has to clear a restart loop. |
| `/check`    | `GET`  | 600/min | Called once per command invocation in a busy bot.             |
| `/votes`    | `GET`  | 60/min  | A log you poll, not a hot path.                               |
| `/widget`   | `GET`  | none    | Public, unauthenticated, cached 5 minutes.                    |

A `429` carries `retryAfter` in seconds. Back off on it rather than retrying
immediately — the limiter is per token, so a tight retry loop only spends your
own budget.

<Note>
  If our Redis is down the limiter fails **open**, not closed. A cache outage
  must not stop a running bot from posting its count; the token still has to be
  valid either way.
</Note>
