Gateway

The Gateway API allows clients to open secure WebSocket connections with Discord to receive events about actions that take place in resources they have access to, like when a channel is updated or a role is created. There are a few cases where clients will also use Gateway connections to update or request resources, like when updating voice state.

The Gateway is Discord's form of real-time communication used by clients. The API for interacting with Gateways is complex and fairly unforgiving, so be sure to read the following documentation in its entirety so you understand the sacred secrets of the Gateway.

The documentation herein is only for the latest version of the API, unless otherwise specified.

Gateway Events

Gateway events are payloads sent over a Gateway connection—either from a client to Discord, or from Discord to a client. A client typically sends events when connecting and managing its connection to the Gateway, and receives events when listening to actions taking place in a resource.

All Gateway events are encapsulated in a Gateway event payload.

A full list of Gateway events and their details are in the Gateway events documentation.

Details about Gateway event payloads are in the Gateway events documentation.

Sending Events

When sending a Gateway event (like when performing an initial handshake or updating presence), your client must send an event payload object with a valid Opcode (op) and inner data object (d).

Event payloads sent over a Gateway connection:

  1. Must be serialized in plain-text JSON or binary ETF.
  2. Must not exceed 15 KiB. If an event payload does exceed this limit, the connection will be closed with a 4002 close code.

All events that you can send over the Gateway are in the Gateway events documentation.

Receiving Events

Receiving a Gateway event from Discord (like when a reaction is added to a message) is much more common (and slightly more complex) than sending them.

While some events are sent to your client regardless of intents, the receiving of most events is configurable by specifying intents when identifying. Intents are bitwise values that can be ORed (|) to indicate which events (or groups of events) you want Discord to send your client. Intents are most commonly used (and required for) bots, as user accounts usually want to receive all event groups, and avoid unnecessary events using other functionality. A list of intents and their corresponding events are listed in the intents section.

When receiving events, you can also configure how events will be sent to your client, like the encoding and compression, or whether sharding should be enabled.

All events that you can receive over the Gateway are in the Gateway events documentation.

Dispatch Events

Opcode 0 Dispatch events are the most common type of event you will receive. Gateway events which represent actions taking place in a guild will be sent to your client as Dispatch events.

When your client is parsing a Dispatch event:

  • The t field can be used to determine which Gateway event the payload represents and the data you can expect in the d field.
  • The s field represents the sequence number of the event, which is the relative order in which it occurred. You need to cache the most recent non-null s value for heartbeats, and to pass when Resuming a connection.

Connections

Gateway connections are persistent WebSockets which introduce more complexity than sending HTTP requests. When interacting with the Gateway, your client must know how to open the initial connection, as well as maintain it and handle any disconnects.

Connection Lifecycle

At a high-level, Gateway connections consist of the following cycle:

Flowchart with an overview of Gateway connection lifecycle

  1. Client establishes a connection with the Gateway after fetching and caching a WebSocket URL using the Get Gateway or Get Gateway Bot endpoint.
  2. Discord sends the client an Opcode 10 Hello event containing a heartbeat interval in milliseconds. Read the section on connecting.
  3. Client starts the heartbeat task: it sends an Opcode 1 Heartbeat event, then continue to send them every heartbeat interval until the connection is closed.
    • Discord will respond to each Heartbeat event with an Opcode 11 Heartbeat ACK event to confirm it was received. If the client doesn't receive a Heartbeat ACK before the next heartbeat interval, it should close the connection and reconnect.
    • Discord may send the client an Opcode 1 Heartbeat event, in which case it should respond with an Opcode 1 Heartbeat event immediately.
  4. Client sends an Opcode 2 Identify event to perform the initial handshake with the Gateway.
  5. Discord sends the client a Ready event which indicates the handshake was successful and the connection is established. The Ready event contains a resume_gateway_url that the client should keep track of to determine the WebSocket URL it should use to resume. Read the section on the Ready event.
  6. The connection may be dropped for a variety of reasons. Whether the client can resume the connection or whether it must re-identify is determined by a variety of factors like the OPcode and close code that it receives. Read the section on disconnecting.
  7. If the client can resume/reconnect, it should open a new connection using resume_gateway_url, then send an Opcode 6 Resume event. If it cannot resume/reconnect, it should open a new connection using the cached URL from step #1, then repeat the whole Gateway cycle. Yipee!

Connecting

Before you can establish a connection to the Gateway, you should call the Get Gateway or the Get Gateway Bot endpoint. Either endpoint will return a payload with a url field whose value is the URL you can use to open a WebSocket connection. In addition to the URL, Get Gateway Bot contains additional information about the recommended number of shards and the session start limits for your user.

When initially calling either Get Gateway or Get Gateway Bot, you should cache the value of the url field and only re-request it after failing to establish a connection to the Gateway.

When connecting to the URL, it's a good idea to explicitly pass the API version and encoding as query parameters. You can also optionally include whether Discord should compress data that it sends your app. For example, wss://gateway.discord.gg/?v=9&encoding=json&compress=zlib-stream is a URL a client may use to connect to the Gateway.

Query String Params
FieldTypeDescription
vintegerAPI Version to use
encodingstringThe encoding of received gateway packets (json or etf)
compress?stringThe optional transport compression of gateway packets (zlib-stream)

Hello Event

Once connected, the client will receive an Opcode 10 Hello event, with information on the connection's heartbeat interval (heartbeat_interval).

The heartbeat interval indicates the how often (in milliseconds) you must send a heartbeat in order to maintain the active connection. Heartbeating is detailed in the sending heartbeats section.

Sending Heartbeats

Heartbeats are pings used to let Discord know that your client is still actively using a Gateway connection. After connecting to the Gateway, your client should send heartbeats (as described below) in a background process until the Gateway connection is closed.

Heartbeat Interval

Upon receiving Opcode 10 Hello, your client should immediately send its first Opcode 1 Heartbeat event (with optional jitter). From that point until the connection is closed, your client must continually send Discord a heartbeat every heartbeat_interval milliseconds. If your client fails to send a heartbeat event in time, your connection will be closed and you will be forced to resume.

When sending a heartbeat, your client will need to include the last sequence number your app received in the d field. The sequence number is sent in every Dispatch event payload (in the s field). If you haven't received any events yet, you should pass null in the d field.

You can send heartbeats before the heartbeat_interval elapses, but you should avoid doing so unless necessary. There is already tolerance in the heartbeat_interval that will cover network latency, so you don't need to account for it in your implementation.

When you send a heartbeat, the Gateway will respond with an Opcode 11 Heartbeat ACK, which is an acknowledgement that the heartbeat was received.

If a client does not receive a heartbeat ACK between its attempts at sending heartbeats, this may be due to a failed or "zombied" connection. The client should immediately terminate the connection with any close code besides 1000 or 1001, then reconnect and attempt to resume.

Heartbeat Requests

In addition to the Heartbeat interval, the Gateway may request additional heartbeats from your client by sending an Opcode 1 Heartbeat event. Upon receiving the event, your client should immediately heartbeat without waiting the remainder of the current interval.

Just like normal, the Gateway will respond with an Opcode 11 Heartbeat ACK event.

Identifying

After the connection is open and you are heartbeating, your client should send an Opcode 2 Identify. This event is the initial handshake with the Gateway that's required before your client can begin sending or receiving most Gateway events.

Users are limited by maximum concurrency (in the session start limit object) when identifying. If your client exceeds this limit, the Gateway will respond with an Opcode 9 Invalid Session event.

After your client sends a valid identify payload, the Gateway will respond with a Ready event which indicates a successfully-connected state with the Gateway. The Ready event is sent as a standard Opcode 0 Dispatch.

Ready Event

As mentioned above, the Ready event is sent to your client after a successful identification. The Ready event includes state, like the guilds you are in, that you need to start interacting with the rest of the platform.

The Ready event also includes fields that you'll need to cache in order to eventually resume your connection after disconnects. Two fields in particular are important to call out:

  • resume_gateway_url is a WebSocket URL that you should use to resume after a disconnect. The resume_gateway_url should be used instead of the URL used when connecting.
  • session_id is the ID for the Gateway session for the connection. It's required to know which stream of events were associated with your connection.

Full details about the Ready event are in the Gateway events documentation.

Disconnecting

The Internet is a scary place. Gateway disconnects happen for a variety of reasons, whether initiated by Discord or by you.

Handling a Disconnect

Due to Discord's architecture, disconnects are a semi-regular event and should be expected and handled. When your client encounters a disconnect, it will typically be sent a close code which can be used to determine whether you can reconnect and resume the session, or whether you have to start over and re-identify.

After you determine whether or not you can reconnect, you must do one of the following:

  • If you determine that you can reconnect and resume the previous session, then you should reconnect using the resume_gateway_url and session_id from the Ready event. Details about when and how to resume can be found in the resuming section.
  • If you cannot reconnect or the reconnect fails, you should open a new connection using the cached URL from the initial call to Get Gateway or Get Gateway Bot. In the case of a failed reconnect, you'll have to re-identify after opening a new connection.

A full list of the close codes can be found in the close codes documentation.

Initiating a Disconnect

When you close the connection to the gateway with close code 1000 or 1001, your session will be invalidated and your user will appear offline.

If you simply close the TCP connection or use a different close code, the session will remain active and timeout after a few minutes. This can be useful when you're resuming the previous session.

Resuming

When your client is disconnected, Discord has a process for reconnecting and resuming, which allows your client to replay any lost events starting from the last sequence number it received. After resuming, you will receive the missed events in the same way you would have had the connection had stayed active. Unlike the initial connection, your client does not need to re-identify when resuming.

There are a handful of scenarios when you should attempt to resume:

  1. You receive an Opcode 7 Reconnect event
  2. You're disconnected with a close code that indicates you can reconnect, or don't receive any close code.
  3. You receives an Opcode 9 Invalid Session event with the d field set to true. This is an unlikely scenario, but it is possible.

Preparing to Resume

Before your client can send an Opcode 6 Resume event, it will need three values: the session_id and the resume_gateway_url from the Ready event, and the sequence number (s) from the last Opcode 0 Dispatch event it received before the disconnect.

After the connection is closed, your client should open a new connection using resume_gateway_url rather than the URL used to initially connect. If you don't use the resume_gateway_url when reconnecting, you will experience disconnects at a higher rate than normal.

Once the new connection is opened, your client should send an Opcode 6 Resume event using the session_id and seq mentioned above.

When resuming, you do not need to send an identify event after opening the connection.

If successful, the Gateway will send the missed events in order, finishing with a Resumed event to signal event replay has finished and that all subsequent events will be new.

If the client does not reconnect in time, the replay buffer will overfill and the session will be invalidated. In this case, the client will receive an Opcode 9 Invalid Session and should disconnect. After disconnect, the client should create a new connection with the cached URL from the Get Gateway or the Get Gateway Bot endpoint, then identify.

Gateway Capabilities

The Discord Gateway provides a system for enabling and disabling custom feature flags on a per-connection basis. These flags, or capabilities, are passed in the capabilities parameter when identifying. Capabilities are a way for clients to opt into payload and functionality changes in a backwards-compatible way, or disable Gateway features they do not need. Capabilities are not at all related to Gateway intents.

The effects of specific capabilities are documented in the Gateway events section, under every event that is affected. However, the documentation generally assumes clients opt into all new feature capabilities.

List of Capabilities

Below is a list of all capabilities and a general description of their effects. For more information, see the documentation for the specific event or feature that is affected.

ValueNameDescription
1 << 0LAZY_USER_NOTESRemoves the notes field from the Ready event,
1 << 1NO_AFFINE_USER_IDSPrevents member/presence syncing and Presence Update events for implicit relationships
1 << 2VERSIONED_READ_STATESEnables versioned read states, changing the read_state field in the Ready event to an object, allowing it to be cached when re-identifying
1 << 3VERSIONED_USER_GUILD_SETTINGSEnables versioned user guild settings, changing the user_guild_settings field in the Ready event to an object, allowing it to be cached when re-identifying
1 << 4DEDUPE_USER_OBJECTSDehydrates the Ready payload, moving all user objects to the users field and replacing them in various places in the payload with user_id or recipient_id, and merging the members fields of all guilds into a single merged_members field
1 << 5PRIORITIZED_READY_PAYLOAD 1Separates the Ready payload into two parts (Ready and Ready Supplemental) allowing the client to receive the Ready payload faster and then receive the rest of the payload later
1 << 6MULTIPLE_GUILD_EXPERIMENT_POPULATIONSChanges the populations entry of guild_experiments in the Ready event to be an array of populations rather than a single population
1 << 7NON_CHANNEL_READ_STATESIncludes read states tied to non-channel resources (e.g. guild scheduled events and notification center) in the read_states field of the Ready event
1 << 8AUTH_TOKEN_REFRESHEnables auth token refresh, allowing the client to optionally receive a new auth token in the auth_token field of the Ready event
1 << 9USER_SETTINGS_PROTORemoves the user_settings field from the Ready event, and prevents User Settings Update events; the user_settings_proto field and User Settings Proto Update event is used instead
1 << 10CLIENT_STATE_V2Enables client state caching v2
1 << 11PASSIVE_GUILD_UPDATEEnables passive guild updates, allowing the client to receive Passive Update v1 events instead of Channel Unreads Update events for guilds it is not subscribed to
1 << 12AUTO_CALL_CONNECTConnects the client to all pre-existing calls upon connecting to the Gateway; this means clients will receive Call Create events for all calls created before the Gateway connection was established without needing to send a Request Call Connect first
1 << 13DEBOUNCE_MESSAGE_REACTIONSDebounces message reaction events, preventing the client from receiving multiple Message Reaction Add events for the same message within a short period of time; clients will receive a single Message Reaction Add Many event instead
1 << 14PASSIVE_GUILD_UPDATE_V2 2Enables passive guild updates v2, allowing the client to receive Passive Update v2 events instead of Channel Unreads Update events for guilds it is not subscribed to

1 Requires DEDUPE_USER_OBJECTS. Without it, the capability will be ignored.

2 Supersedes PASSIVE_GUILD_UPDATE. If enabled, PASSIVE_GUILD_UPDATE will be ignored.

{/ todo: link events here /}

Gateway Intents

Maintaining a stateful application can be difficult when it comes to the amount of data you're expected to process over a Gateway connection, especially at scale. Gateway intents are a system to help you lower the computational burden.

Intents are bitwise values passed in the intents parameter when identifying which correlate to a set of related events. For example, the event sent when a guild is created (Guild Create) and when a channel is updated (Channel Update) both require the same GUILDS (1 << 0) intent (as listed in the table below). If you do not specify an intent when identifying, you will not receive any of the Gateway events associated with that intent.

For bots, two types of intents exist:

  • Standard intents can be passed by default. You don't need any additional permissions or configurations.
  • Privileged intents must be enabled before they can be used. More information about privileged intents can be found in their section below.

Your Gateway connection will be closed if you pass invalid intents (4013 close code), or a privileged intent that hasn't been configured or approved for your bot (4014 close code). User accounts do not require approval and can use any intent, albeit with caveats of their own.

List of Intents

Below is a list of all intents and the Gateway events associated with them. Any events not listed means it's not associated with an intent and will always be sent to your client.

All events, including those that aren't associated with an intent, are in the Gateway events documentation.

GUILDS (1 << 0)
- GUILD_CREATE
- GUILD_UPDATE
- GUILD_DELETE
- GUILD_ROLE_CREATE
- GUILD_ROLE_UPDATE
- GUILD_ROLE_DELETE
- CHANNEL_CREATE
- CHANNEL_UPDATE
- CHANNEL_DELETE
- CHANNEL_PINS_UPDATE
- THREAD_CREATE
- THREAD_UPDATE
- THREAD_DELETE
- THREAD_LIST_SYNC
- THREAD_MEMBER_UPDATE
- THREAD_MEMBERS_UPDATE ¹
- STAGE_INSTANCE_CREATE
- STAGE_INSTANCE_UPDATE
- STAGE_INSTANCE_DELETE
GUILD_MEMBERS (1 << 1)
- GUILD_MEMBER_ADD
- GUILD_MEMBER_UPDATE
- GUILD_MEMBER_REMOVE
- THREAD_MEMBERS_UPDATE ¹
GUILD_MODERATION (1 << 2)
- GUILD_AUDIT_LOG_ENTRY_CREATE
- GUILD_BAN_ADD
- GUILD_BAN_REMOVE
GUILD_EMOJIS_AND_STICKERS (1 << 3)
- GUILD_EMOJIS_UPDATE
- GUILD_STICKERS_UPDATE
GUILD_INTEGRATIONS (1 << 4)
- GUILD_INTEGRATIONS_UPDATE
- INTEGRATION_CREATE
- INTEGRATION_UPDATE
- INTEGRATION_DELETE
GUILD_WEBHOOKS (1 << 5)
- WEBHOOKS_UPDATE
GUILD_INVITES (1 << 6)
- INVITE_CREATE
- INVITE_DELETE
GUILD_VOICE_STATES (1 << 7)
- VOICE_STATE_UPDATE
- VOICE_CHANNEL_EFFECT_SEND
GUILD_PRESENCES (1 << 8) ²
- PRESENCE_UPDATE
GUILD_MESSAGES (1 << 9)
- MESSAGE_CREATE
- MESSAGE_UPDATE
- MESSAGE_DELETE
- MESSAGE_DELETE_BULK
GUILD_MESSAGE_REACTIONS (1 << 10)
- MESSAGE_REACTION_ADD
- MESSAGE_REACTION_REMOVE
- MESSAGE_REACTION_REMOVE_ALL
- MESSAGE_REACTION_REMOVE_EMOJI
GUILD_MESSAGE_TYPING (1 << 11)
- TYPING_START
DIRECT_MESSAGES (1 << 12)
- MESSAGE_CREATE
- MESSAGE_UPDATE
- MESSAGE_DELETE
- CHANNEL_PINS_UPDATE
DIRECT_MESSAGE_REACTIONS (1 << 13)
- MESSAGE_REACTION_ADD
- MESSAGE_REACTION_REMOVE
- MESSAGE_REACTION_REMOVE_ALL
- MESSAGE_REACTION_REMOVE_EMOJI
DIRECT_MESSAGE_TYPING (1 << 14)
- TYPING_START
MESSAGE_CONTENT (1 << 15) ³
GUILD_SCHEDULED_EVENTS (1 << 16)
- GUILD_SCHEDULED_EVENT_CREATE
- GUILD_SCHEDULED_EVENT_UPDATE
- GUILD_SCHEDULED_EVENT_DELETE
- GUILD_SCHEDULED_EVENT_USER_ADD
- GUILD_SCHEDULED_EVENT_USER_REMOVE
AUTO_MODERATION_CONFIGURATION (1 << 20)
- AUTO_MODERATION_RULE_CREATE
- AUTO_MODERATION_RULE_UPDATE
- AUTO_MODERATION_RULE_DELETE
AUTO_MODERATION_EXECUTION (1 << 21)
- AUTO_MODERATION_ACTION_EXECUTION
GUILD_MESSAGE_POLLS (1 << 24)
- MESSAGE_POLL_VOTE_ADD
- MESSAGE_POLL_VOTE_REMOVE
DIRECT_MESSAGE_POLLS (1 << 25)
- MESSAGE_POLL_VOTE_ADD
- MESSAGE_POLL_VOTE_REMOVE

1 Thread Members Update contains different data depending on which intents are used.

2 For bots, events under the GUILD_PRESENCES and GUILD_MEMBERS intents are turned off by default on all API versions. Bots using API v7 and below will receive events associated with the privileged intents they have without passing those intents into the intents parameter when identifying. Bots using API v8 and above must specify all intents when identifying (privileged or not). User accounts default to all intents being on.

3 MESSAGE_CONTENT does not represent individual events, but rather affects what data is present for events that could contain message content fields. More information is in the message content intent section.

Caveats

Any events not defined in an intent are considered "passthrough" and will always be sent to you.

Guild Member Update is sent for current-user updates regardless of whether the GUILD_MEMBERS intent is set.

Guild Create and Request Guild Members are uniquely affected by intents. See these sections for more information.

Thread Members Update by default only includes if the current user was added to or removed from a thread. To receive these updates for other users, request the GUILD_MEMBERS Gateway Intent.

Privileged Intents

Some intents are defined as privileged due to the sensitive nature of the data. Currently, those intents include:

For bots, to specify privileged intents in their IDENTIFY payload, they must be authorized for them. Verified applications can only use privileged intents after they've been approved for them. Unverified applications may simply toggle them on. User accounts do not have the contept of privileged intents, and can simply use any intent; however, they have caveats of their own.

Enabling Privileged Intents

Before bots can use privileged intents, they must be enabled in their application's flags. The limited variants of these intents (usable by unverified applications) can simply be toggled on. The full variants (usable by verified applications) require approval.

Applications that qualify for verification must first be verified, and you can request access to these intents during the verification process. If the app is already verified and you need to request additional privileged intents, you can contact support.

Gateway Restrictions

Privileged intents affect which Gateway events bots are permitted to receive. For bots using API v8 and above, all intents (privileged and not) must be specified in the intents parameter when identifying. Passing a privileged intent in the intents parameter without having it enabled will lead to the Gateway connection being closed with a (4014 close code).

HTTP Restrictions

In addition to Gateway restrictions, privileged intents also affect the HTTP API endpoints bots are permitted to call, and the data they can receive. For example, to use the Get Guild Members endpoint, a bot must have the GUILD_MEMBERS intent enabled. If the bot does not have the intent enabled, the endpoint will return a 403 Forbidden error.

HTTP API restrictions are independent of Gateway restrictions, and are unaffected by intents passed in the intents parameter when identifying.

Message Content Intent

MESSAGE_CONTENT (1 << 15) is a unique privileged intent that isn't directly associated with any Gateway events. Instead, access to MESSAGE_CONTENT permits users to receive message content data across the APIs.

While user accounts can also toggle the MESSAGE_CONTENT intent, they are not subject to the same restrictions as bots. User accounts do not have the concept of privileged intents, and therefore do not need to be approved for the intent. Additionally, user accounts must explicitly toggle on the intent in all API versions if they are utilizing intents.

Any fields affected by the message content intent are noted in the relevant documentation. For example, the content, embeds, attachments, components, and poll fields in message objects all contain message content and therefore require the intent.

Users without the intent will receive empty values in fields that contain user-inputted content with a few exceptions:

Rate Limiting

Clients can send 120 Gateway events per connection every 60 seconds, meaning an average of 2 commands per second. Clients that surpass the limit are immediately disconnected from the Gateway. Similar to other rate limits, repeat offenders will have their API access revoked.

Clients also have a limit for concurrent identify requests allowed per 5 seconds. If you hit this limit, the Gateway will respond with an Opcode 9 Invalid Session.

Encoding and Compression

When establishing a connection to the Gateway, clients can use the encoding parameter to choose whether to communicate with Discord using a plain-text JSON or binary ETF encoding. You can pick whichever encoding type you're more comfortable with, but both have their own quirks. If you aren't sure which encoding to use, JSON is generally recommended.

Clients can also optionally enable compression to receive zlib-compressed packets. Payload compression can only be enabled when using JSON encoding, but transport compression can be used regardless of encoding type.

Payload Compression

Payload compression enables optional per-packet compression for some events when Discord is sending events over the connection.

Payload compression uses the zlib format (see RFC1950 2.2) when sending payloads. To enable payload compression, your client can set compress to true when identifying. Note that even when payload compression is enabled, not all payloads will be compressed.

When payload compression is enabled, your client must detect and decompress these payloads to plain-text JSON before attempting to parse them. If you are using payload compression, the Gateway does not implement a shared compression context between events sent.

Payload compression will be disabled if you use transport compression.

Using ETF Encoding

When using ETF (External Term Format) encoding, there are some specific behaviors you should know:

  • Snowflake IDs are transmitted as 64-bit integers or strings.
  • Your client can't send compressed messages to the server.
  • When sending payloads, you must use string keys. Using atom keys will result in a 4002 close code.

See erlpack for an ETF implementation example.

Transport Compression

Transport compression enables optional compression for all packets when Discord is sending events over the connection. The only currently-available transport compression option is zlib-stream.

When transport compression is enabled, your client needs to process received data through a single Gateway connection using a shared zlib context. However, each Gateway connection should use its own unique zlib context.

When processing transport-compressed data, you should push received data to a buffer until you receive the 4-byte Z_SYNC_FLUSH suffix (00 00 ff ff). After you receive the Z_SYNC_FLUSH suffix, you can then decompress the buffer.

Transport Compression Example
# Z_SYNC_FLUSH suffix
ZLIB_SUFFIX = b'\x00\x00\xff\xff'
# initialize a buffer to store chunks
buffer = bytearray()
# create a shared zlib inflation context to run chunks through
inflator = zlib.decompressobj()
# ...
def on_websocket_message(msg):
# always push the message data to your cache
buffer.extend(msg)
# check if the last four bytes are equal to ZLIB_SUFFIX
if len(msg) < 4 or msg[-4:] != ZLIB_SUFFIX:
return
# if the message *does* end with ZLIB_SUFFIX,
# get the full message by decompressing the buffers
# NOTE: the message is utf-8 encoded.
msg = inflator.decompress(buffer)
buffer = bytearray()
# here you can treat `msg` as either JSON or ETF encoded,
# depending on your `encoding` param

Tracking State

Most of a client's state is provided during the initial Ready event (and optionally the Guild Create events that immediately follow).

As resources continue to be created, updated, and deleted, Gateway events are sent to notify the client of these changes and to provide associated data. To avoid excessive API calls, clients should cache as many relevant resource states as possible, and update them as new events are received.

An example of state tracking can be considered in the case of a bot that wants to track member status: when initially connecting to the Gateway, the bot will receive information about the online status of guild members (whether they're online, idle, on do-not-disturb, or offline). To keep the state updated, the bot will track and parse Presence Update events as they're received, then update the cached member objects accordingly.

Guild Availability

When connecting to the Gateway as a bot user, guilds that the bot is a part of will start out as unavailable. Don't fret! The Gateway will automatically attempt to reconnect on your behalf. As guilds become available to you, you will receive Guild Create events. On the other hand, users start out with all possible guilds available to them.

Sharding

As bots grow and are added to an increasing number of guilds, some developers may find it necessary to break or split portions of their bots operations into separate logical processes. As such, the Gateway implements a method of user-controlled guild sharding which allows for splitting events across a number of Gateway connections. Guild sharding is entirely user controlled, and requires no state-sharing between separate connections to operate.

Sharding is required for all bots in more than 2500 guilds. While all bots and user accounts can utilize sharding, it is primarily intended for large bots. It is never necessary for user accounts, as they are limited to a maximum of 200 guilds.

To enable sharding on a connection, the client should send the shard array in the Identify payload. The first item in this array should be the zero-based integer value of the current shard, while the second represents the total number of shards. DMs will only be sent to shard 0.

To calculate which events will be sent to which shard, the following formula can be used:

Sharding Formula
shard_id = (guild_id >> 22) % num_shards

As an example, if you wanted to split the connection between three shards, you'd use the following values for shard for each connection: [0, 3], [1, 3], and [2, 3]. Note that only the first shard ([0, 3]) would receive DMs.

Note that num_shards does not relate to (or limit) the total number of potential sessions. It is only used for routing traffic. As such, sessions do not have to be identified in an evenly distributed manner when sharding. You can establish multiple sessions with the same [shard_id, num_shards], or sessions with different num_shards values. This allows you to create sessions that will handle more or less traffic than others for more fine-tuned load balancing, or to orchestrate "zero-downtime" scaling/updating by handing off traffic to a new deployment of sessions with a higher or lower num_shards count that are prepared in parallel.

Max Concurrency

If you have multiple shards, you may start them concurrently based on the max_concurrency value returned to you on session start. Which shards you can start concurrently are assigned based on a key for each shard. The rate limit key for a given shard can be computed with

rate_limit_key = shard_id % max_concurrency

This puts your shards into "buckets" of max_concurrency size. When you start your bot, you may start up to max_concurrency shards at a time, and you must start them by "bucket" in order. To explain another way, let's say you have 16 shards, and your max_concurrency is 16:

shard_id: 0, rate limit key (0 % 16): 0
shard_id: 1, rate limit key (1 % 16): 1
shard_id: 2, rate limit key (2 % 16): 2
shard_id: 3, rate limit key (3 % 16): 3
shard_id: 4, rate limit key (4 % 16): 4
shard_id: 5, rate limit key (5 % 16): 5
shard_id: 6, rate limit key (6 % 16): 6
shard_id: 7, rate limit key (7 % 16): 7
shard_id: 8, rate limit key (8 % 16): 8
shard_id: 9, rate limit key (9 % 16): 9
shard_id: 10, rate limit key (10 % 16): 10
shard_id: 11, rate limit key (11 % 16): 11
shard_id: 12, rate limit key (12 % 16): 12
shard_id: 13, rate limit key (13 % 16): 13
shard_id: 14, rate limit key (14 % 16): 14
shard_id: 15, rate limit key (15 % 16): 15

You may start all 16 of your shards at once, because each has a rate_limit_key which fills the bucket of 16 shards. However, let's say you had 32 shards:

shard_id: 0, rate limit key (0 % 16): 0
shard_id: 1, rate limit key (1 % 16): 1
shard_id: 2, rate limit key (2 % 16): 2
shard_id: 3, rate limit key (3 % 16): 3
shard_id: 4, rate limit key (4 % 16): 4
shard_id: 5, rate limit key (5 % 16): 5
shard_id: 6, rate limit key (6 % 16): 6
shard_id: 7, rate limit key (7 % 16): 7
shard_id: 8, rate limit key (8 % 16): 8
shard_id: 9, rate limit key (9 % 16): 9
shard_id: 10, rate limit key (10 % 16): 10
shard_id: 11, rate limit key (11 % 16): 11
shard_id: 12, rate limit key (12 % 16): 12
shard_id: 13, rate limit key (13 % 16): 13
shard_id: 14, rate limit key (14 % 16): 14
shard_id: 15, rate limit key (15 % 16): 15
shard_id: 16, rate limit key (16 % 16): 0
shard_id: 17, rate limit key (17 % 16): 1
shard_id: 18, rate limit key (18 % 16): 2
shard_id: 19, rate limit key (19 % 16): 3
shard_id: 20, rate limit key (20 % 16): 4
shard_id: 21, rate limit key (21 % 16): 5
shard_id: 22, rate limit key (22 % 16): 6
shard_id: 23, rate limit key (23 % 16): 7
shard_id: 24, rate limit key (24 % 16): 8
shard_id: 25, rate limit key (25 % 16): 9
shard_id: 26, rate limit key (26 % 16): 10
shard_id: 27, rate limit key (27 % 16): 11
shard_id: 28, rate limit key (28 % 16): 12
shard_id: 29, rate limit key (29 % 16): 13
shard_id: 30, rate limit key (30 % 16): 14
shard_id: 31, rate limit key (31 % 16): 15

In this case, you must start the shard buckets in "order". That means that you can start shard 0 -> shard 15 concurrently, and then you can start shard 16 -> shard 31.

Sharding for Large Bots

For bots in more than 150,000 guilds, there are some additional considerations you must take around sharding. Discord will migrate your bot to large bot sharding when it starts to get near the large bot sharding threshold. The bot owner(s) will receive a system DM and email confirming this move has completed as well as what shard number has been assigned.

The number of shards you run must be a multiple of the shard number provided when reaching out to you. If you attempt to start your bot with an invalid number of shards, your Gateway connection will close with a 4010 close code.

The Get Gateway Bot endpoint will always return the correct amount of shards, so if you're already using this endpoint to determine your number of shards, you shouldn't require any changes.

The session start limit for these bots will also be increased from 1000 to max(2000, (guild_count / 1000) * 5) per day. You also receive an increased max_concurrency, the number of shards you can concurrently start.

Session Start Limit Object

Session Start Limit Structure
FieldTypeDescription
totalintegerTotal number of session starts the user is allowed
remainingintegerRemaining number of session starts the user is allowed
reset_afterintegerNumber of milliseconds after which the limit resets
max_concurrencyintegerNumber of identify requests allowed per 5 seconds

Endpoints

Get Gateway

GET/gateway

Returns an object with a single valid WebSocket URL, which the client can use for Connecting. Clients should cache this value and only call this endpoint to retrieve a new URL if they are unable to properly establish a connection using the cached one.

Response Body
FieldTypeDescription
urlstringThe WebSocket URL that can be used for connecting to the Gateway
Example Response
{
"url": "wss://gateway.discord.gg"
}

Get Gateway Bot

GET/gateway/bot

Returns an object based on the information in Get Gateway, plus additional metadata that can help during the operation of large or sharded bots. Unlike Get Gateway, this route should not be cached for extended periods of time as the value is not guaranteed to be the same per-call, and changes as the user joins/leaves guilds.

Response Body
FieldTypeDescription
urlstringThe WebSocket URL that can be used for connecting to the Gateway
shardsintegerThe recommended number of shards to use when connecting
session_start_limitsession start limit objectInformation on the current session start limit
Example Response
{
"url": "wss://gateway.discord.gg",
"shards": 9,
"session_start_limit": {
"total": 1000,
"remaining": 999,
"reset_after": 14400000,
"max_concurrency": 1
}
}