REST and ratelimits

Every REST call funnels through one RequestHandler. The helpers on Client and on structures (createMessage, editChannel, member.ban, ...) all use it.

Every REST call funnels through one RequestHandler. The helpers on Client and on structures (createMessage, editChannel, member.ban, ...) all use it.

Issuing requests

You normally call the typed helpers, not the handler directly. To reach an endpoint Athena has not wrapped:

import { Routes } from 'discord-api-types/v10';
const channel = await client.requestHandler.request('GET', Routes.channel(channelID), true);

Signature: request(method, url, auth?, body?, file?, route?, short?). For GET/DELETE, body keys become the query string. auth: true adds the Authorization header.

File uploads

await client.createMessage(channelID, { content: 'Look' }, { file: buffer, name: 'a.png' });
await client.createMessage(channelID, { content: 'Look' }, [
  { file: b1, name: 'a.png' },
  { file: b2, name: 'b.png' }
]);

MIME type is sniffed from the filename. Multiple files become files[0], files[1], etc.

Ratelimit model

Discord limits per route bucket. Athena tracks each route with its own sequential bucket, so same-route requests run strictly in order, and updates buckets from the X-RateLimit-* headers.

On a 429: it reads Retry-After (or X-RateLimit-Reset-After), engages a global block if the limit was global, and re-queues the request in a priority slot so it keeps its place.

Latency compensation

By default Athena tracks a rolling average of request latency and folds it into reset timing to absorb clock skew. Disable with rest.disableLatencyCompensation: true. rest.latencyThreshold (default 30000 ms) is where it warns about excessive latency.

Connection reuse

Athena defaults to a shared keep-alive HTTP(S) agent, so requests reuse connections instead of doing a fresh TLS handshake each time. This matters a lot at volume. Supplying your own rest.agent overrides it.

Force-queueing until ready

rest.forceQueueing: true holds authenticated requests until the first shardPreReady, avoiding a startup race against Discord's identify queue.

Retry behaviour

  • 429: re-queued after the reset interval (with a clamp so a missing or malformed Retry-After cannot cause a tight loop).
  • 502: up to 4 retries with random 100 to 2000 ms backoff.
  • other 5xx: thrown as DiscordHTTPError.
  • 4xx: thrown as DiscordRESTError with Discord's code and message. See Errors.

Tracing

client.on('rawREST', (e) => console.log(e.method, e.url, e.resp.statusCode, e.latency));

Endpoints

Endpoints holds URL builders (for example Endpoints.CHANNEL_MESSAGES(channelID)) and CDN helpers. They return paths, not method-typed calls; response types come from discord-api-types.

Tips

  • Always await REST calls; forgetting it loses the ratelimit feedback path.
  • Prefer bulk endpoints (bulkEditCommands) over loops.
  • Check the cache before REST: channel.messages.get(id) is microseconds, the REST round trip is tens of milliseconds.