Roody REST API v1

Connect your Discord server to scripts and external applications. Read access and, since v1.1, write access (economy) protected by scopes. Available on Roody Pro and Team.

Quick start

Generate a key in API keys and make your first request:

curl https://dashboard.roody.es/api/v1/me \
  -H "Authorization: Bearer rdy_TU_CLAVE_AQUI"

Or in JavaScript:

const res = await fetch('https://dashboard.roody.es/api/v1/me', {
  headers: { 'X-API-Key': 'rdy_TU_CLAVE_AQUI' }
});
const { data } = await res.json();
console.log(data.user.username, data.plan.tier);

Authentication

Every request to /api/v1/* has to carry the key in one of these two headers:

Authorization: Bearer rdy_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
X-API-Key:     rdy_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Keys are personal, non-transferable, and never shown again after you create them — keep them in a secret manager. If a key is compromised, revoke it from your panel and create a new one.

Quotas by plan

Each key has a per-minute request limit that depends on the owner's plan. If you go over it, we return 429 with the Retry-After header.

Pro
60 req/min
Enough for syncing dashboards or cron jobs.
Team
600 req/min
For multi-server integrations and production tooling.

Every response includes the X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset headers so your client knows how much you have left.

Endpoints

GET /api/v1/me Account data

Returns the user who owns the key, their plan, the per-minute quota and the metadata of the key used in the request.

{
  "success": true,
  "data": {
    "user": {
      "discordId": "123456789012345678",
      "username": "tu_usuario"
    },
    "plan": {
      "tier": "pro",
      "name": "Pro"
    },
    "rateLimit": {
      "perMinute": 60,
      "advanced": false
    },
    "apiKey": {
      "prefix": "rdy_aB3xF2gH",
      "label": "Mi script",
      "createdAt": "2026-05-24T10:00:00.000Z",
      "lastUsedAt": "2026-05-24T10:42:11.000Z"
    }
  }
}
GET /api/v1/bot/status Account data

Current bot status (online/connecting), number of servers and users, gateway ping and uptime in milliseconds.

{
  "success": true,
  "data": {
    "status": "online",
    "servers": 1284,
    "users": 458321,
    "ping": 41,
    "uptimeMs": 184302000
  }
}
GET /api/v1/guilds Account data

Servers where the account owning the key is the owner or has "Administrator". Includes name, icon, member count and role.

{
  "success": true,
  "data": {
    "count": 2,
    "guilds": [
      {
        "id": "987654321098765432",
        "name": "Mi servidor",
        "icon": "https://cdn.discordapp.com/...",
        "memberCount": 348,
        "role": "owner"
      },
      {
        "id": "876543210987654321",
        "name": "Otro servidor",
        "icon": null,
        "memberCount": 1240,
        "role": "admin"
      }
    ]
  }
}
GET /api/v1/guilds/:id Account data

Detailed server data: description, icon, banner, members, channels, roles, boost level, creation date, locale, features.

{
  "success": true,
  "data": {
    "id": "987654321098765432",
    "name": "Mi servidor",
    "memberCount": 348,
    "channels": 32,
    "roles": 18,
    "boost": {
      "level": 2,
      "count": 7
    },
    "owner": {
      "id": "123456789012345678"
    },
    "role": "owner"
  }
}
GET /api/v1/guilds/:id/config Account data

A summary of the server's enabled modules and whether they have a channel assigned: welcome, goodbye, boost, moderation, logs, giveaways, polls, tickets, antiraid, levels, economy.

{
  "success": true,
  "data": {
    "guildId": "987654321098765432",
    "lang": "es",
    "modules": {
      "bienvenidas": {
        "enabled": true,
        "hasChannel": true,
        "type": "embed"
      },
      "moderacion": {
        "enabled": true,
        "antiflood": true,
        "antilinks": false,
        "antimayus": false
      },
      "economia": {
        "enabled": false
      }
    }
  }
}
GET /api/v1/guilds/:id/economy/users/:userId Account data

Wallet, bank and basic economy data for a user in the server. Requires the economy:read scope (keys with * include it).

{
  "success": true,
  "data": {
    "userId": "123456789012345678",
    "cash": 1500,
    "bank": 8000,
    "total": 9500,
    "level": 7,
    "netWorth": 9500
  }
}
POST /api/v1/guilds/:id/economy/users/:userId/balance Account data

Changes a user's balance. JSON body: { "action": "add" | "remove" | "set", "amount": number ≥ 0, "target": "cash" | "bank" (optional, cash by default) }. Requires the economy:write scope. add/remove are clamped to [0, 10,000,000,000].

{
  "success": true,
  "data": {
    "userId": "123456789012345678",
    "cash": 2500,
    "bank": 8000,
    "total": 10500,
    "level": 7,
    "netWorth": 9500,
    "action": "add",
    "target": "cash",
    "applied": 1000
  }
}
PATCH /api/v1/guilds/:id/config/:module Account data

Turns a server module on or off. JSON body: { "enabled": true | false }. Supported modules: bienvenidas, despedidas, boost, logs, sorteos, polls, tickets, antiraid, niveles, economia. Requires the config:write scope.

{
  "success": true,
  "data": {
    "guildId": "987654321098765432",
    "module": "economia",
    "enabled": true
  }
}
POST /api/v1/guilds/:id/members/:userId/warn Account data

Records a warning. JSON body: { "reason": "text" } (required). Requires the moderation:write scope.

{
  "success": true,
  "data": {
    "userId": "123456789012345678",
    "warnId": "A1B2C3",
    "reason": "Spam en general",
    "totalWarns": 2
  }
}
POST /api/v1/guilds/:id/members/:userId/timeout Account data

Applies a timeout. JSON body: { "durationSeconds": 1..2419200, "reason": "text" (optional) }. The bot needs "Moderate Members" and has to sit above the user. Scope moderation:write.

{
  "success": true,
  "data": {
    "userId": "123456789012345678",
    "action": "timeout",
    "durationSeconds": 3600,
    "reason": "Flood"
  }
}
POST /api/v1/guilds/:id/members/:userId/kick Account data

Kicks the user. JSON body: { "reason": "text" (optional) }. The bot needs "Kick Members" and hierarchy over the user. Scope moderation:write.

{
  "success": true,
  "data": {
    "userId": "123456789012345678",
    "action": "kick",
    "reason": "Saltarse las normas"
  }
}
POST /api/v1/guilds/:id/members/:userId/ban Account data

Bans the user (it also works as a pre-emptive ban if they are not in the server). JSON body: { "reason": "text" (optional), "deleteMessageSeconds": 0..604800 (optional) }. The bot needs "Ban Members". Scope moderation:write.

{
  "success": true,
  "data": {
    "userId": "123456789012345678",
    "action": "ban",
    "reason": "Raid",
    "deleteMessageSeconds": 86400
  }
}

Real-world uses

Complete examples of things you can automate. Switch language with the buttons and replace rdy_TU_CLAVE, GUILD_ID and USER_ID with your own.

1. Reward a user

Your site or game gives a member 500 coins when they complete an action. (scope economy:write)

curl -X POST https://dashboard.roody.es/api/v1/guilds/GUILD_ID/economy/users/USER_ID/balance \
  -H "Authorization: Bearer rdy_TU_CLAVE" \
  -H "Content-Type: application/json" \
  -d '{"action":"add","amount":500,"target":"cash"}'
const res = await fetch(
  'https://dashboard.roody.es/api/v1/guilds/GUILD_ID/economy/users/USER_ID/balance',
  {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer rdy_TU_CLAVE',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ action: 'add', amount: 500, target: 'cash' })
  }
);
const { data } = await res.json();
console.log('New balance:', data.cash);
import requests

res = requests.post(
    'https://dashboard.roody.es/api/v1/guilds/GUILD_ID/economy/users/USER_ID/balance',
    headers={'Authorization': 'Bearer rdy_TU_CLAVE'},
    json={'action': 'add', 'amount': 500, 'target': 'cash'},
)
print('New balance:', res.json()['data']['cash'])

2. Ban from your own panel

An anti-cheat or an external panel bans a cheater and deletes their messages from the last day. (scope moderation:write)

curl -X POST https://dashboard.roody.es/api/v1/guilds/GUILD_ID/members/USER_ID/ban \
  -H "Authorization: Bearer rdy_TU_CLAVE" \
  -H "Content-Type: application/json" \
  -d '{"reason":"Cheating detected","deleteMessageSeconds":86400}'
await fetch(
  'https://dashboard.roody.es/api/v1/guilds/GUILD_ID/members/USER_ID/ban',
  {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer rdy_TU_CLAVE',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ reason: 'Cheating detected', deleteMessageSeconds: 86400 })
  }
);
import requests

requests.post(
    'https://dashboard.roody.es/api/v1/guilds/GUILD_ID/members/USER_ID/ban',
    headers={'Authorization': 'Bearer rdy_TU_CLAVE'},
    json={'reason': 'Cheating detected', 'deleteMessageSeconds': 86400},
)

3. Switch a module off for maintenance

Before an event, you disable the economy from your script and turn it back on afterwards. (scope config:write)

curl -X PATCH https://dashboard.roody.es/api/v1/guilds/GUILD_ID/config/economia \
  -H "Authorization: Bearer rdy_TU_CLAVE" \
  -H "Content-Type: application/json" \
  -d '{"enabled":false}'
await fetch(
  'https://dashboard.roody.es/api/v1/guilds/GUILD_ID/config/economia',
  {
    method: 'PATCH',
    headers: {
      'Authorization': 'Bearer rdy_TU_CLAVE',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ enabled: false })
  }
);
import requests

requests.patch(
    'https://dashboard.roody.es/api/v1/guilds/GUILD_ID/config/economia',
    headers={'Authorization': 'Bearer rdy_TU_CLAVE'},
    json={'enabled': False},
)

4. Check a user's balance

Show on your site how much money a member has. (scope economy:read)

curl https://dashboard.roody.es/api/v1/guilds/GUILD_ID/economy/users/USER_ID \
  -H "Authorization: Bearer rdy_TU_CLAVE"
const res = await fetch(
  'https://dashboard.roody.es/api/v1/guilds/GUILD_ID/economy/users/USER_ID',
  { headers: { 'Authorization': 'Bearer rdy_TU_CLAVE' } }
);
const { data } = await res.json();
console.log(`${data.cash} in wallet, ${data.bank} in bank`);
import requests

res = requests.get(
    'https://dashboard.roody.es/api/v1/guilds/GUILD_ID/economy/users/USER_ID',
    headers={'Authorization': 'Bearer rdy_TU_CLAVE'},
)
data = res.json()['data']
print(data['cash'], 'in wallet,', data['bank'], 'in bank')

5. Respect the rate limit (429)

If you make a lot of calls in a row, on a 429 wait the seconds given in Retry-After and retry.

# --retry retries and respects the Retry-After header
curl --retry 3 --retry-delay 0 \
  https://dashboard.roody.es/api/v1/me \
  -H "Authorization: Bearer rdy_TU_CLAVE"
async function call(url, opts) {
  for (let i = 0; i < 4; i++) {
    const res = await fetch(url, opts);
    if (res.status !== 429) return res;
    const wait = Number(res.headers.get('Retry-After') || 1);
    await new Promise(r => setTimeout(r, wait * 1000));
  }
  throw new Error('Rate limit: out of retries');
}
import time, requests

def call(method, url, **kw):
    for _ in range(4):
        res = requests.request(method, url, **kw)
        if res.status_code != 429:
            return res
        time.sleep(int(res.headers.get('Retry-After', 1)))
    raise RuntimeError('Rate limit: out of retries')

Error format

Every error follows the same shape: success: false + error.code + error.message.

{
  "success": false,
  "error": {
    "code": "tier_insufficient",
    "message": "This key belongs to a user without API access.",
    "currentTier": "free",
    "requiredTier": "pro"
  }
}

The most common error codes:

HTTPcodeWhen it happens
401missing_api_keyThe authentication header is missing.
401invalid_api_keyKey not found or revoked.
401expired_api_keyThe key has expired.
403tier_insufficientThe key's owner doesn't have Pro/Team.
403guild_access_deniedThe key's owner doesn't administer that server.
403insufficient_scopeThe key doesn't include the required scope (economy:write, for example).
400invalid_action / invalid_amount / invalid_user_idInvalid write parameters (economy).
400invalid_module / invalid_bodyUnsupported module or invalid body (config).
404config_not_foundThe server has no configuration.
400invalid_reason / invalid_durationThe moderation parameters are missing or invalid.
403target_is_owner / target_is_bot / hierarchy_error / bot_missing_permissionThat user can't be moderated (owner, the bot itself, hierarchy, or the bot is missing a permission).
404member_not_foundThe user isn't in the server (for timeout/kick).
502action_failedDiscord rejected the moderation action.
404guild_not_foundThe bot isn't in the requested server.
404economy_user_not_foundThe user has no economy data in that server.
404endpoint_not_foundThat endpoint doesn't exist in v1.
429rate_limitedYou have gone over your per-minute quota.
503bot_unavailableThe bot is offline.