REST API v1 · Developers
BotGG developer API: connect your Discord server to anything
The BotGG REST API lets your own website, game server or backend read and change your Discord server’s BotGG data — pay out Bank prizes, sync XP from your game, reward invites or show leaderboards on your site. Create a key in the console, send one HTTPS request, done.
- Base URL:
https://console.botgg.app/api/v1 - JSON over HTTPS,
Authorization: BearerAPI keys with per-resource scopes - A machine-readable OpenAPI 3.1 description of every endpoint
- Free plan: 2 keys, 60 requests per minute per key
Developer docs
On this page
Overview
What members do with BotGG in Discord — balances, levels, invites and more — is also available to your own code. Each part of BotGG is a resource with its own endpoints and scopes; the API reference lists them all.
- Base URL:
https://console.botgg.app/api/v1. HTTPS only. - Requests and responses are JSON (
Content-Type: application/json). - IDs are Discord snowflakes (17–20 digits) and are always sent and returned as strings.
- Every key belongs to one server and works only for that server — see authentication.
- Server-to-server only: requests from browsers are rejected, so keys never end up in front-end code.
Quick start
- Open the console, pick your server and go to API in the sidebar (you need the Manage Server permission).
- Create a key: give it a name (for example “Website”), choose the scopes and an optional expiry. Copy the key right away — it is shown only once.
- Store it as a secret on your server, for example in the
BOTGG_API_KEYenvironment variable. - Make your first request — here, read a member’s Bank balance (scope
bank:read).
curl -s https://console.botgg.app/api/v1/guilds/123456789012345678/bank/members/876543210987654321 \
-H "Authorization: Bearer $BOTGG_API_KEY"{
"userId": "876543210987654321",
"balance": 1250,
"currency": { "name": "Coin", "plural": "Coins", "emoji": "🪙" }
}Every resource needs its feature enabled on the server; otherwise its endpoints answer 409 FEATURE_DISABLED (BANK_DISABLED for the Bank). All error codes are on the errors page.
API reference by resource
One reference page per resource, generated from the same registry that runs the API — parameters, request and response fields, scopes, error codes and ready-to-run curl, JavaScript and Python examples:
All endpoints
All paths are relative to https://console.botgg.app/api/v1. {guildId} is your server’s ID and {userId} is a member’s Discord ID. Endpoints marked Sensitive need a scope that acts on members or exposes moderation data.
The same description is available as an OpenAPI 3.1 document — import it into Postman, Insomnia or a client generator. It is served live (no key needed) at https://console.botgg.app/api/v1/openapi.json.
How the API behaves
- Authentication and API keys — key format, scopes, expiry, revoking and security practices.
- Errors — the error format and every error code with its HTTP status.
- Rate limits — requests per minute per key by plan and the
X-RateLimit-*headers. - Idempotent retries — the
Idempotency-Keyheader that makes retried writes safe. - Webhooks — signed events BotGG sends to your server, with verification code.
- AI assistants and MCP — using the API from AI tools.
Complete examples
A complete balance change with one idempotency key per change, waiting on 429/503 and handling INSUFFICIENT_FUNDS. Run it on your server, never in the browser.
// Node.js 22+ (built-in fetch). Run it on your server — never in a browser or game client.
const API = 'https://console.botgg.app/api/v1';
const KEY = process.env.BOTGG_API_KEY; // a key with the bank:write scope, kept in a secret
const GUILD_ID = '123456789012345678';
export async function changeBalance(userId, type, amount, reason) {
// One Idempotency-Key per logical change: every retry below reuses it, so the change happens once.
const idempotencyKey = crypto.randomUUID();
for (let attempt = 1; attempt <= 3; attempt++) {
const res = await fetch(`${API}/guilds/${GUILD_ID}/bank/members/${userId}/transactions`, {
method: 'POST',
headers: {
Authorization: `Bearer ${KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': idempotencyKey,
},
body: JSON.stringify({ type, amount, reason }),
});
const data = await res.json();
if (res.ok) return data; // 201 — or the original response, replayed
if (res.status === 429 || res.status === 503) {
const seconds = Number(res.headers.get('Retry-After')) || attempt * 2;
await new Promise((resolve) => setTimeout(resolve, seconds * 1000));
continue;
}
if (data.error === 'INSUFFICIENT_FUNDS') {
throw new Error(`Not enough balance: the member has ${data.balance}`);
}
throw new Error(`${res.status} ${data.error}: ${data.message}`);
}
throw new Error('BotGG API is busy, try again later');
}
const tx = await changeBalance('876543210987654321', 'add', 100, 'Tournament prize');
console.log(`New balance: ${tx.balance}`);# Python 3 + requests. Run it on your server — never ship the key to clients.
import os, time, uuid
import requests
API = "https://console.botgg.app/api/v1"
KEY = os.environ["BOTGG_API_KEY"] # a key with the bank:write scope
GUILD_ID = "123456789012345678"
def change_balance(user_id, type_, amount, reason=None):
idempotency_key = str(uuid.uuid4()) # one per logical change, reused on every retry
for attempt in range(1, 4):
res = requests.post(
f"{API}/guilds/{GUILD_ID}/bank/members/{user_id}/transactions",
headers={"Authorization": f"Bearer {KEY}", "Idempotency-Key": idempotency_key},
json={"type": type_, "amount": amount, "reason": reason},
timeout=10,
)
data = res.json()
if res.ok:
return data # 201 — or the original response, replayed
if res.status_code in (429, 503):
time.sleep(int(res.headers.get("Retry-After", attempt * 2)))
continue
raise RuntimeError(f"{res.status_code} {data['error']}: {data['message']}")
raise RuntimeError("BotGG API is busy, try again later")
print(change_balance("876543210987654321", "add", 100, "Tournament prize")["balance"])