Skip to content

Snowflakes

A snowflake is a 64-bit identifier for a TEBHOS resource such as a user, guild, channel, message, role, emoji or attachment. It is the type written as snowflake in every field table of this reference, and the HTTP API and the Gateway share the same identifier space.

A snowflake is unique within TEBHOS. The value is stable, and it is not reused after the resource is deleted.

Never parse a snowflake as a JSON number

Snowflake values exceed the exact integer range of a JSON double, so parsing one as a number can silently change it. Parse the string into a 64-bit integer (BigInt in JavaScript, int in Python), and compare snowflakes as integers, because decimal strings of different lengths do not sort correctly as text.

Some identifiers are not snowflakes. Invite codes, vanity URL codes, webhook tokens and upload keys are opaque strings, and each resource page says so where it applies.

Format

A snowflake packs a timestamp, a worker ID and a sequence into 64 bits. The worker and sequence fields distinguish identifiers issued during the same millisecond. Bit 63 stays zero until the year 2084, so every snowflake issued today fits a signed 64-bit integer.

Field Bits Description
Timestamp 63 to 22 Milliseconds since 2015-01-01T00:00:00.000Z
Worker ID 21 to 12 The ID generator worker, from 0 through 1023
Sequence 11 to 0 The per-worker sequence within one millisecond, from 0 through 4095

The epoch is 1420070400000 milliseconds after the Unix epoch. The lower 22 bits describe how the identifier was allocated. Do not use them for routing or for any resource meaning.

Identifiers are issued by a dedicated generator service, so they increase monotonically per worker. Treat the embedded timestamp as an approximate creation time, not as proof that the resource exists.

Extracting a timestamp

To obtain the creation time in Unix milliseconds, shift the snowflake right by 22 bits and add the epoch.

timestamp_ms = (snowflake >> 22) + 1420070400000

The inverse produces the smallest possible snowflake for a Unix millisecond timestamp. It is useful as a pagination boundary.

snowflake = (timestamp_ms - 1420070400000) << 22
const TEBHOS_EPOCH = 1420070400000n;

function snowflakeToDate(id) {
  return new Date(Number((BigInt(id) >> 22n) + TEBHOS_EPOCH));
}

function dateToSnowflake(date) {
  return ((BigInt(date.getTime()) - TEBHOS_EPOCH) << 22n).toString();
}

snowflakeToDate("1489002177550843904");
from datetime import datetime, timezone

TEBHOS_EPOCH = 1420070400000

def snowflake_to_datetime(snowflake: str) -> datetime:
    ms = (int(snowflake) >> 22) + TEBHOS_EPOCH
    return datetime.fromtimestamp(ms / 1000, tz=timezone.utc)

def datetime_to_snowflake(dt: datetime) -> str:
    return str((int(dt.timestamp() * 1000) - TEBHOS_EPOCH) << 22)

Wire representation

TEBHOS emits a snowflake as an unsigned decimal string in every JSON body and Gateway payload, for example "id": "1489002177550843904". Path parameters and query string parameters are always text.

Send snowflakes the same way:

  • In a path, use the decimal digits, as in GET /channels/1489002177550843904.
  • In a JSON body or query string, send a decimal string.

A path or body value that does not parse as a 64-bit integer is rejected, usually with 400 BAD_REQUEST. A well-formed ID that names no visible resource returns the ordinary not-found result for that resource, usually 404 NOT_FOUND.

Pagination

A collection that pages over a snowflake-ordered resource accepts a cursor such as before, after or around. Each operation defines which cursors it supports, whether they can be combined, the result order and how limit applies.

Cursor Selects
before Identifiers lower than the cursor value, excluding it
after Identifiers higher than the cursor value, excluding it
around A window centred on the cursor value

List channel messages accepts all three. The bulk message fetch in Messages accepts them on each entry. Some cursors are not snowflakes: for example the pinned message list pages by pin time. Each operation states the type of its own cursors.

Because the timestamp occupies the high bits, numeric snowflake order is creation order at millisecond resolution.

A cursor is a numeric boundary

A cursor does not need to identify an existing resource. A snowflake computed from a timestamp can delimit a time range, for example "messages after midnight yesterday".