REST API
cordless has a complete typed client for Discord’s REST API. Every endpoint is reachable up to three ways, all calling the same underlying request logic. This page explains the difference and which to reach for.
Three layers
Section titled “Three layers”Flat _rest functions (cordless._rest.<resource>.<function>) build the actual request: cordless._rest.channels.fetch_channel(channel_id, token=None). This is the plumbing every other layer delegates to. You will not normally call these directly, and they are not listed in the REST API Reference, which covers the two public layers below.
bot.<verb>() mixin methods on Cordless are a thin delegation to the flat functions: await bot.fetch_channel(channel_id). Reach for these when you have an id but not the object it belongs to, for example inside a slash command handler that only received a channel_id from the interaction payload.
Object-method sugar on the model classes (Guild, Channel, Member, Message, …) is the same call again, bound to an object you already have: await channel.fetch(), await channel.edit(name="new-name"), await guild.create_channel(name="general", type=0), await message.reply(content="hi"). This is the default choice whenever you already hold the object, it reads closer to what it does and doesn’t require threading an id through by hand.
Both public layers, per Discord resource, with full signatures: REST API Reference.
All three call the same request logic underneath, so retries, rate limiting, and error handling below apply identically regardless of which layer you use.
Errors
Section titled “Errors”A REST or webhook call that fails raises DiscordHTTPError, or a status-specific subclass: BadRequest (400), Unauthorized (401), Forbidden (403), NotFound (404), or ServerError (5xx). Each carries .status, .body, and .headers from the response, so a handler that needs more than the message text still has it.
import cordless
try: await channel.fetch()except cordless.NotFound: await ctx.send("That channel doesn't exist any more.")except cordless.DiscordHTTPError as exc: await ctx.send(f"Discord said no: {exc.status}")DiscordHTTPError is a cordless.CordlessError, not a RuntimeError. If you’re catching RuntimeError around a REST call from an earlier cordless version, switch to cordless.DiscordHTTPError or one of its subclasses.
Every exception type, with its docstring: Errors.
Retrying after a network error
Section titled “Retrying after a network error”Every REST call retries once after a transient network error (a dropped connection, not an HTTP error response), but only for methods considered safe to repeat: GET, HEAD, OPTIONS, PUT, and DELETE. POST and PATCH are not retried automatically, since Discord gives no guarantee that resending one is safe if the first attempt actually reached Discord and was processed before the connection dropped, resending could duplicate whatever it did, like sending the same message twice.
PUT is assumed safe by default because every PUT endpoint cordless currently wraps genuinely is (setting a value, or an “ends up in state X” add that no-ops on repetition). The underlying idempotent= keyword on cordless._rest._client.request()/request_raw() exists so a future PUT endpoint that isn’t safe to repeat can be marked as such internally. It isn’t currently exposed through the flat _rest functions, the bot.<verb>() mixin, or object-method sugar, since nothing wrapped today needs it.
See Rate Limiting for what happens on a 429 specifically, and the CLI Reference for deploy/destroy flags.