WEIR

Twitter Snowflake ID: How to Decode It to a Timestamp

Decode a Twitter snowflake ID to the millisecond in Python or JavaScript, proved on real @NASA posts, plus the month account IDs began to hold dates.


Published · Updated · 7 min read

Cover image: The clock inside every post ID

A Twitter snowflake ID is a post's numeric ID, and its top bits store the exact millisecond the post was created.

Every post on X (Twitter) since November 2010 has one. If you just want a date, paste a post link or ID into our free tweet ID to date converter. You need no code, and it runs in your browser.

Or do the math yourself in Python:

from datetime import datetime, timezone

ms = (2119136607009734978 >> 22) + 1288834974657
print(datetime.fromtimestamp(ms / 1000, timezone.utc))
2026-11-07 18:35:13.422000+00:00

How a Twitter snowflake ID is built

A snowflake ID packs three numbers into one 64-bit integer: a time, a machine and a counter.

The 64 bits of a snowflake ID
Timestamp, ms since the epoch41 bits
Sequence, counts within 1 ms12 bits
Machine, datacenter + worker10 bits
Unused top bit, always 01 bit

Twitter's engineering blog announced Snowflake on 1 June 2010, built from "timestamp, worker number and sequence number." The archived source code sets the numbers behind the chart:

  • twepoch = 1288834974657L is the epoch: 01:42:54.657 UTC on 4 November 2010.
  • workerIdBits and datacenterIdBits are 5L each: the 10 machine bits.
  • sequenceBits = 12L, so the time sits 22 bits up.

The README says that 41-bit clock "gives us 69 years." By our math, it runs out in July 2080.

The repo's README now calls Twitter's internal Snowflake "a full rewrite." The time bits still decode, but you should treat the low 22 bits as opaque.

Step 1: Decode a Twitter snowflake to a timestamp in Python

Shift the ID right by 22 bits, add the epoch, and read it as UTC, with the standard library only:

from datetime import datetime, timezone

EPOCH_MS = 1288834974657  # twepoch in Twitter's Snowflake code

def snowflake_time(x_id):
    ms = (int(x_id) >> 22) + EPOCH_MS
    return datetime.fromtimestamp(ms / 1000, timezone.utc)

for x_id in ["2119136607009734978", "2019881223666233717", "2115480207469560109"]:
    print(x_id, snowflake_time(x_id).isoformat(timespec="milliseconds"))

We ran it on 7 November 2026 and got:

2119136607009734978 2026-11-07T18:35:13.422+00:00
2019881223666233717 2026-02-06T21:09:45.957+00:00
2115480207469560109 2026-10-28T16:25:59.839+00:00

You can pass the ID as a string or an int. Python's int has no size limit, so the ID never loses digits.

Step 2: Decode it in JavaScript with BigInt

In JavaScript you need BigInt, written with an n suffix. Every post ID made after 28 November 2010 is too big for a plain Number to hold exactly: X's docs say it's "limited to 53 bits."

const EPOCH = 1288834974657n; // twepoch in Twitter's Snowflake code

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

for (const id of ["2119136607009734978", "2019881223666233717", "2115480207469560109"]) {
  console.log(id, snowflakeTime(id).toISOString());
}

Node 22 printed:

2119136607009734978 2026-11-07T18:35:13.422Z
2019881223666233717 2026-02-06T21:09:45.957Z
2115480207469560109 2026-10-28T16:25:59.839Z

Proof: decoded IDs match X's own timestamps

On 7 November 2026, we fetched the first post on the @NASA, @XDevelopers and @github timelines through Weir: three pinned posts, for 12 credits.

Each decoded time matched Weir's createdAt to the second:

Account Post ID Decoded Weir createdAt
@NASA 2119136607009734978 2026-11-07 18:35:13.422 2026-11-07 18:35:13
@XDevelopers 2019881223666233717 2026-02-06 21:09:45.957 2026-02-06 21:09:45
@github 2115480207469560109 2026-10-28 16:25:59.839 2026-10-28 16:25:59

createdAt stops at whole seconds, so the ID gives you the sharper clock. The media IDs we tested were snowflakes too: the @NASA video's decoded to 24 seconds before its post.

Which IDs are snowflakes? Posts since 2010, accounts since 2016

Posts switched first. Twitter's API team gave developers the date: "Snowflake will be turned on" on "4th November 2010 (Thursday)."

The same note calls the IDs before it "sequential," so you'll find no time in them. Account IDs switched more than five years later, as six public accounts show:

Account Created (UTC) ID digits Holds the time?
@NASA 19 Dec 2007 8 No
@XDevelopers 14 Dec 2013 10 No
@OpenAI 6 Dec 2015 10 No
@LaLaLand 4 Jan 2016 10 No
@arrivalmovie 14 Jun 2016 18 Yes, to the second
@AnthropicAI 25 Jan 2021 19 Yes, 12 s early

A counter ID decodes to seconds after the epoch: @NASA's user ID lands at 01:42:54 UTC on 4 November 2010. That date tells you there's no time inside.

So from about March 2016 on, an 18- or 19-digit user ID gives you the sign-up time, give or take seconds. For an older account, our free account age checker reads X's stored date after a free sign-in, and our guide to when someone joined Twitter covers other routes.

X's docs call Space IDs "Alphanumeric, not Snowflake format." We didn't test List or DM IDs.

Turn a time into an ID: how since_id and max_id windows work

Run it backwards and you get the smallest ID X could make at a moment: subtract the epoch, then shift left 22 bits.

from datetime import datetime, timedelta, timezone

EPOCH_MS = 1288834974657
UNIX = datetime(1970, 1, 1, tzinfo=timezone.utc)

def first_id_at(iso):
    ms = (datetime.fromisoformat(iso) - UNIX) // timedelta(milliseconds=1)
    return (ms - EPOCH_MS) << 22

start = first_id_at("2026-11-07T18:35:13+00:00")
end = first_id_at("2026-11-07T18:35:14+00:00")
print(start, end)
print(start <= 2119136607009734978 < end)
print(f"from:NASA since_id:{start - 1} max_id:{end - 1}")
2119136605238198272 2119136609432502272
True
from:NASA since_id:2119136605238198271 max_id:2119136609432502271

X's since_id: keeps posts newer than an ID, and max_id: keeps that ID and older. So you subtract 1 from each bound.

We ran that query through Weir's search on 7 November 2026. It returned exactly one post, @NASA's, for 2 credits. Both operators also passed our X search operator tests on 27 October.

Which bound should you use?

  • Whole seconds: since_time: and until_time: take Unix seconds and are simpler.
  • Milliseconds or X's API: use IDs, since X's search endpoint takes since_id and until_id.
  • Weir: its since and until fields turn into ID bounds for you.

Common mistakes: precision, seconds and time zones

The first two slips, run on @NASA's ID:

const body = '{"id": 2119136607009734978, "id_str": "2119136607009734978"}';
const post = JSON.parse(body);

console.log(post.id); // digits lost
console.log(post.id_str); // exact
console.log(post.id >> 22); // a 32-bit shift
console.log(new Date(Number((BigInt(post.id_str) >> 22n) + 1288834974657n)).toISOString());
2119136607009735000
2119136607009734978
205
2026-11-07T18:35:13.422Z
  • Parsing the ID as a Number. JSON.parse changed it. Read the string: id_str in X's v1.1, id in Weir and v2, as our tweet JSON example shows.
  • >> on a Number. JavaScript shifts Numbers as 32-bit integers, which is how you get 205. Shift a BigInt.
  • Seconds where milliseconds belong. new Date() reads its input as milliseconds, so a seconds value lands in January 1970.
  • The wrong time zone. Decoded times are UTC. Day operators like since: follow Japan time through Weir, but ID bounds ignore time zones.

How the Twitter snowflake algorithm generates unique IDs

Each ID server makes IDs alone, with no central counter: "an uncoordinated approach," in Twitter's words. The code takes four steps:

  1. Read the clockMilliseconds since the epoch.
  2. Shift it up 22 bitsThe time fills the top of the number.
  3. Add the machineA 5-bit datacenter ID and a 5-bit worker ID.
  4. Add the sequenceA 12-bit count within the same millisecond.

Two guards stop duplicates. Past 4,096 IDs in one millisecond, a worker waits for the next one. If the clock runs backwards, it refuses: "Clock moved backwards."

IDs are only "roughly sortable," to about a second, so don't trust ID order for events milliseconds apart. If you build your own unique ID generator this way, keep both guards.

Caveats: when the ID won't help, and when to skip Weir

We run Weir, so here's the honest list:

  • You don't need an API for post dates. The converter or the Step 1 script gives them free.
  • Old accounts hold no date. Before about March 2016, read the Joined line or call a profile API, such as Weir's at 3 credits.
  • The low 22 bits have no public map, so don't build on them.
  • Weir is read-only. It reads public data and isn't licensed by X, so access can change.

FAQ

What is a Twitter snowflake ID?

It's the 64-bit number X gives each post, built from a millisecond timestamp, a machine number and a counter. Twitter turned it on for posts on 4 November 2010 so its servers could make unique, time-ordered IDs.

How do you convert a Twitter snowflake to a timestamp?

Shift the ID right by 22 bits. Then add X's epoch, 1288834974657, and you have Unix time in milliseconds, in UTC. In JavaScript, shift a BigInt, never a Number.

Is there a free Twitter snowflake ID decoder?

Yes. Our tweet ID to date converter decodes a post link or ID in your browser, with no sign-in. It also reads 18- and 19-digit account IDs.

Does a Twitter user ID show when the account was created?

Only if the account was made from about March 2016 on. Its ID then has 18 or 19 digits and decodes to the sign-up, within seconds. Older accounts have shorter counter IDs with no date.

Sources

Checked 7 November 2026 (UTC):