Skip to content

How to decode a Twitter Snowflake ID

To decode a Twitter Snowflake ID, shift the number right by 22 bits and add 1288834974657: the result is the Unix time, to a thousandth of a second, when the post or account was created, exact for every post since November 4, 2010.

Short answer: A Twitter Snowflake ID keeps its creation time in its top 41 bits, counted in thousandths of a second from X's epoch, November 4, 2010. Posts have one since that day, new accounts since February 1, 2016; older IDs hold no date. Sorsa API's /tweet-info-bulk dates 100 post IDs per request, from $0.014 per 1,000; 100 free requests cover 10K.

Endpoint
POST /tweet-info-bulk
Page size
100 post IDs per request
Price
from $0.014 per 1,000

#Decode a Snowflake ID to a date

A tweet's creation time is written into its ID, so you can read it without fetching the post: drop the low 22 bits of the ID, add X's epoch, and you have the Unix time in UTC, exact to a thousandth of a second, finer than the minute X prints under a post. It works on deleted posts too, as long as you have the ID, and the free Tweet ID to Date Converter does it for one ID or a whole list.

  1. Copy the ID. In a post link such as x.com/<username>/status/2110301305616889042, the ID is the number after status/. The same number opens the post on its own at x.com/i/web/status/ followed by the ID.

  2. Drop the low 22 bits. 2110301305616889042 >> 22 is 503135038761: the time between X's epoch and the post, in thousandths of a second. Dividing by 2^22 and rounding down gives the same number.

  3. Add the epoch. 503135038761 + 1288834974657 = 1791970013418, a Unix time in thousandths of a second: 1791970013.418 in seconds.

  4. Read it as a date. 1791970013.418 is Wednesday, October 14, 2026, 09:26:53.418 UTC. Convert it to your own zone for display: X shows each reader the time in their local zone.

Paste any post ID, user ID or post link into the decoder to see its date and its four parts.

A 19-digit Snowflake ID, made on October 14, 2026.

Created, in UTC Wednesday, October 14, 2026, 09:26:53.418
Your time zone
October 14, 2026, 09:26:53 UTC
Unix time, seconds
1791970013.418
Size
19 digits, 61 bits
Timestamp
503135038761
thousandths of a second after the epoch
Datacenter
11
bits 21 to 17
Worker
7
bits 16 to 12
Sequence
1234
bits 11 to 0

The decoder reads the date and the four bit fields of any X ID in your browser, without a request.

The same arithmetic in code. Python's integers hold all 64 bits; in JavaScript use BigInt, because a plain number rounds a 19-digit ID.

from datetime import datetime, timezone

EPOCH = 1288834974657  # ms: November 4, 2010, 01:42:54.657 UTC


def snowflake_to_datetime(snowflake_id):
    ms = (int(snowflake_id) >> 22) + EPOCH
    return datetime.fromtimestamp(ms / 1000, tz=timezone.utc)


print(snowflake_to_datetime("2110301305616889042"))
# 2026-10-14 09:26:53.418000+00:00

In a database that stores IDs as 64-bit integers, the same shift runs inside the query:

SQL
-- PostgreSQL, id stored as BIGINT
SELECT id,
       to_timestamp(((id >> 22) + 1288834974657) / 1000.0) AS created_utc
FROM posts;

-- BigQuery, id stored as INT64
SELECT id,
       TIMESTAMP_MILLIS((id >> 22) + 1288834974657) AS created_utc
FROM dataset.posts;

The converter also takes a whole column of IDs at once, downloads the results as a CSV and turns a date back into an ID, all in your browser.

#Dates for old IDs and long lists, with Sorsa API

The arithmetic fails on two kinds of ID: posts from before November 4, 2010 and accounts opened before February 2016. Their IDs were counted out one by one and hold no time, so the formula returns a moment within seconds of the epoch, which looks plausible and is wrong. For those IDs, and whenever you want X's own created_at next to the decoded time, look the IDs up in batches.

For dating tweet IDs in bulk, old ones included, use Sorsa API, a Twitter (X) API and scraper for public data: POST /tweet-info-bulk returns created_at for up to 100 post IDs per request, from $0.014 per 1,000 posts, against $5.00 per 1,000 on the official X API. Accounts go through GET /info-batch at the same rate, 100 per request, where X bills $10.00 per 1,000.

  1. Get a key. Sign up and copy your API key from the dashboard. The account comes with 100 free requests, no card needed.

  2. Send up to 100 post IDs. Pass IDs or full links in tweet_links to POST /tweet-info-bulk (opens in a new tab); each post comes back whole, with its author's profile.

    Terminal
    curl -X POST "https://api.sorsa.io/v3/tweet-info-bulk" \
      -H "ApiKey: YOUR_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{"tweet_links": ["1243617904", "2110301305616889042"]}'
    
    Response200 OK
    {
      "tweets": [
        {
          "id": "1243617904",
          "created_at": "2009-02-24T19:51:06Z",
          "full_text": "Field notes from the harbor survey are up on the site",
          "likes_count": 4,
          "user": {
            "id": "20536157",
            "username": "lanternbay_research",
            "created_at": "2009-02-10T16:22:31Z"
          }
        },
        {
          "id": "2110301305616889042",
          "created_at": "2026-10-14T09:26:53Z",
          "full_text": "New dataset: ten years of harbor traffic, in one CSV",
          "likes_count": 212,
          "user": {
            "id": "1427632226255384585",
            "username": "copperfield_signals",
            "created_at": "2021-08-17T14:03:41Z"
          }
        }
      ]
    }
    
  3. Date the accounts the same way. Repeat user_ids once per account, up to 100, in GET /info-batch (opens in a new tab); each profile carries its created_at.

    Terminal
    curl -G "https://api.sorsa.io/v3/info-batch" \
      -H "ApiKey: YOUR_API_KEY" \
      --data-urlencode "user_ids=20536157" \
      --data-urlencode "user_ids=1427632226255384585"
    
  4. Spend requests only where decoding can't help. The script reads post_ids.csv, decodes every Snowflake for free and sends only the older IDs to the API, then writes one date per ID.

    Python
    import csv
    from datetime import datetime, timezone
    
    import requests
    
    BASE = "https://api.sorsa.io/v3"
    HEADERS = {"ApiKey": "YOUR_API_KEY"}
    EPOCH = 1288834974657           # Twitter's epoch, Unix ms
    
    
    def decode(post_id):
        """The UTC time inside a Snowflake ID; None for an older ID."""
        if len(post_id) < 15:       # counted ID from before Nov 4, 2010
            return None
        ms = (int(post_id) >> 22) + EPOCH
        return datetime.fromtimestamp(ms / 1000, tz=timezone.utc)
    
    
    def look_up(post_ids):
        """created_at from Sorsa API, 100 post IDs per request."""
        found = {}
        for i in range(0, len(post_ids), 100):
            r = requests.post(f"{BASE}/tweet-info-bulk", headers=HEADERS,
                              json={"tweet_links": post_ids[i:i + 100]},
                              timeout=30)
            r.raise_for_status()
            for tweet in r.json().get("tweets", []):
                found[tweet["id"]] = tweet["created_at"]
        return found
    
    
    with open("post_ids.csv", newline="", encoding="utf-8") as f:
        ids = [row["post_id"].strip() for row in csv.DictReader(f)]
    
    decoded = {i: decode(i) for i in ids}
    looked_up = look_up([i for i, d in decoded.items() if d is None])
    
    with open("post_dates.csv", "w", newline="", encoding="utf-8") as f:
        out = csv.writer(f)
        out.writerow(["post_id", "created_at_utc", "source"])
        for i in ids:
            if decoded[i]:
                out.writerow([i, decoded[i].isoformat(), "decoded"])
            else:
                out.writerow([i, looked_up.get(i, ""), "looked up"])
    

A post that is deleted or protected doesn't come back, so its row stays empty: match results by id, never by position. A 403 means the key is missing or out of requests, a 429 that you went past 20 requests in one second, and failed requests are never charged.

Looking up old post IDs costs $5.00 per 1,000 on the official X API, which bills every post it returns, and from $0.014 per 1,000 on Sorsa API, which bills the request and packs 100 posts into each one.

Post IDs to date

Official X API

$5,000

for 1M post IDs

$0.005 per post

Sorsa API

from$14

for 1M post IDs

from $0.014 per 1,000

over 250x cheaper

Against the official X API, Sorsa API comes out ahead on each thing this job needs:

What the job needsSorsa APIOfficial X API
Price per 1,000 postsfrom $0.014$5.00
Price per 1,000 accountsfrom $0.014$10.00
IDs per request100 posts or 100 accounts100, billed one by one
Posts from before November 2010yes, with created_atyes, at the same per-post price
Setupone API key in a headerdeveloper account, app, bearer token
Speed20 requests per second on every planrate limits per 15-minute window
Volumeno post-read cap; bigger plans as you grow3M post reads a month, then Enterprise from about $42K a month
Your X loginnever asked forthe developer account sits on your X account
  • Price per 1,000 posts

    Sorsa API
    from $0.014
    Official X API
    $5.00
  • Price per 1,000 accounts

    Sorsa API
    from $0.014
    Official X API
    $10.00
  • IDs per request

    Sorsa API
    100 posts or 100 accounts
    Official X API
    100, billed one by one
  • Posts from before November 2010

    Sorsa API
    yes, with created_at
    Official X API
    yes, at the same per-post price
  • Setup

    Sorsa API
    one API key in a header
    Official X API
    developer account, app, bearer token
  • Speed

    Sorsa API
    20 requests per second on every plan
    Official X API
    rate limits per 15-minute window
  • Volume

    Sorsa API
    no post-read cap; bigger plans as you grow
    Official X API
    3M post reads a month, then Enterprise from about $42K a month
  • Your X login

    Sorsa API
    never asked for
    Official X API
    the developer account sits on your X account

The official X API's prices are on its pricing page (opens in a new tab); the full comparison is in our guide to X API pricing. For whole timelines and searches from 2006 on, see historical Twitter data.

#What the 64 bits of a Snowflake ID hold

A Snowflake ID is a 64-bit integer split into four fields: a 41-bit timestamp, a 5-bit datacenter number, a 5-bit worker number and a 12-bit sequence, with the top bit always 0 so the number stays positive. The layout comes from Twitter's own generator, released in 2010 and kept in the snowflake-2010 source (opens in a new tab), which builds each ID as ((time - epoch) << 22) | (datacenter << 17) | (worker << 12) | sequence.

The 64 bits of the Twitter Snowflake ID 2110301305616889042: a sign bit of 0, 41 bits of milliseconds since the epoch, 5 bits of datacenter, 5 bits of worker and a 12-bit sequence Post ID 2110301305616889042 64 bits, highest first Timestamp: 41 bits 5 5 12 Bit 63 is always 0 Timestamp, bits 62 to 22 503135038761 thousandths of a second after the epoch Datacenter 11 bits 21 to 17 Worker 7 bits 16 to 12 Sequence 1234 bits 11 to 0 503135038761 + 1288834974657 = 1791970013418 Wednesday, October 14, 2026, 09:26:53.418 UTC
The timestamp takes 41 of the 64 bits, so shifting away the low 22 leaves the time since X's epoch, in thousandths of a second.

Each field can be read with a shift and a mask:

BitsWidthFieldRead it withExample value
631Sign, always 00
62 to 2241Time since the epoch, in 1/1000 sid >> 22503135038761
21 to 175Datacenter (0 to 31)(id >> 17) & 3111
16 to 125Worker (0 to 31)(id >> 12) & 317
11 to 012Sequence (0 to 4,095)id & 40951234
  • 63

    Width
    1
    Field
    Sign, always 0
    Read it with
    Example value
    0
  • 62 to 22

    Width
    41
    Field
    Time since the epoch, in 1/1000 s
    Read it with
    id >> 22
    Example value
    503135038761
  • 21 to 17

    Width
    5
    Field
    Datacenter (0 to 31)
    Read it with
    (id >> 17) & 31
    Example value
    11
  • 16 to 12

    Width
    5
    Field
    Worker (0 to 31)
    Read it with
    (id >> 12) & 31
    Example value
    7
  • 11 to 0

    Width
    12
    Field
    Sequence (0 to 4,095)
    Read it with
    id & 4095
    Example value
    1234

The epoch. 1288834974657 is the constant twepoch in that source file: Thursday, November 4, 2010, 01:42:54.657 UTC. Snowflake counts from there instead of from 1970, which leaves more of the 41 bits for the future. The field holds 2^41 thousandths of a second, about 69.7 years, and runs out on July 10, 2080 at 17:30:30.208 UTC.

The other 22 bits. Twitter's 2010 README (opens in a new tab) describes 10 bits of machine ID, up to 1,024 machines, that the code splits into datacenter and worker. The 12-bit sequence numbers the IDs one machine hands out within the same thousandth of a second, up to 4,096 of them. X has never documented what its current datacenter and worker numbers mean, so treat them as labels, not data.

Order. Because the time sits in the highest bits, a larger ID almost always means a newer post. The README promised order to within about one second across machines, and X's ID guide (opens in a new tab) calls IDs roughly time-ordered for that reason: two posts made in the same second on different machines can come out of order.

Other platforms copied the design with an epoch of their own, so an ID from another service, decoded with X's epoch, lands years away from its real date.

#Which X IDs are Snowflakes, and since when

Post IDs became Snowflakes on November 4, 2010; user, list and saved-search IDs followed on February 1, 2016. X announced the second switch itself in a developer forum post of November 23, 2015 (opens in a new tab): starting February 1, 2016, all new users, lists and saved searches get 64-bit Snowflake IDs, posts and DMs already have them, and existing IDs stay valid.

When X IDs became Snowflakes: post IDs on November 4, 2010, user and list IDs from February 1, 2016 Counted IDs before, Snowflake IDs after 2009 Post IDs grow to 64 bits, still counted Jun 1, 2010 Twitter announces Snowflake Nov 4, 2010 Posts get Snowflake IDs The epoch: 01:42:54.657 UTC Oct 21, 2013 User IDs pass 32 bits, still counted Feb 1, 2016 New accounts and lists get Snowflake IDs Old IDs keep their numbers Jul 10, 2080 The 41-bit clock runs out
Two switches matter: posts on November 4, 2010 and accounts on February 1, 2016. Everything older is a counted ID with no date inside.

Two older dates cause most of the confusion. In January 2013 Twitter's developer blog announced 64-bit user IDs (opens in a new tab), switched on October 21, 2013. That change only let the counter grow past 32 bits; accounts kept getting counted IDs until 2016. Pages that put the user switch "around 2020" are wrong as well: an account opened in mid-2016 already has an 18-digit Snowflake.

ObjectSnowflake ID sinceDecodes to a date
Post (tweet)November 4, 2010yes, for IDs of 15 digits or more
Direct messageby November 2015yes, for IDs of 15 digits or more
User accountFebruary 1, 2016yes for 18- and 19-digit IDs, no for older accounts
List, saved searchFebruary 1, 2016yes for the newer, longer IDs
Spaceneverno: Space IDs are letters and digits, such as 1YqGodQbNXDxv
  • Post (tweet)

    Snowflake ID since
    November 4, 2010
    Decodes to a date
    yes, for IDs of 15 digits or more
  • Direct message

    Snowflake ID since
    by November 2015
    Decodes to a date
    yes, for IDs of 15 digits or more
  • User account

    Snowflake ID since
    February 1, 2016
    Decodes to a date
    yes for 18- and 19-digit IDs, no for older accounts
  • List, saved search

    Snowflake ID since
    February 1, 2016
    Decodes to a date
    yes for the newer, longer IDs
  • Space

    Snowflake ID since
    never
    Decodes to a date
    no: Space IDs are letters and digits, such as 1YqGodQbNXDxv

Good to know

A counted ID decodes without an error, which is the trap. X's own example user ID, 2244994945, turns into November 4, 2010, 01:42:55 UTC, under a second after the epoch, and the first post ever, ID 20, decodes to the epoch itself. Any result in the first hours of November 4, 2010 means the ID holds no date.

For an account's registration time to the second, read created_at from a profile lookup even when its ID is a Snowflake: X doesn't document when in sign-up it stamps the ID. Without code, the free Twitter Account Age Checker shows any account's join date.

#How old is an ID? Count its digits

A Snowflake ID grows by one digit each time the clock inside it crosses a power of ten, so its length alone tells you roughly when it was made. Post IDs have been 19 digits long since May 25, 2018, and they stay at 19 until the field runs out in 2080, because the top bit is always 0.

DigitsPost IDsAccount IDs
up to 11before November 4, 2010: counted, no date insidebefore February 2016: counted, no date inside
15November 4 to 6, 2010none
16November 6 to December 1, 2010none
17December 1, 2010 to August 7, 2011none
18August 7, 2011 to May 25, 2018February 1, 2016 to May 25, 2018
19since May 25, 2018since May 25, 2018
  • up to 11

    Post IDs
    before November 4, 2010: counted, no date inside
    Account IDs
    before February 2016: counted, no date inside
  • 15

    Post IDs
    November 4 to 6, 2010
    Account IDs
    none
  • 16

    Post IDs
    November 6 to December 1, 2010
    Account IDs
    none
  • 17

    Post IDs
    December 1, 2010 to August 7, 2011
    Account IDs
    none
  • 18

    Post IDs
    August 7, 2011 to May 25, 2018
    Account IDs
    February 1, 2016 to May 25, 2018
  • 19

    Post IDs
    since May 25, 2018
    Account IDs
    since May 25, 2018

Length is also why IDs break in JavaScript and spreadsheets. Every post ID made since November 28, 2010 is larger than 2^53, the biggest whole number a JavaScript number holds exactly, and that is the reason X's ID guide tells you to keep IDs as strings. Our guide to the Twitter user ID from username shows what Excel and JavaScript do to an ID and how to store one safely.

#Turn a date into an ID for since_id and max_id

The formula runs backwards: subtract the epoch from a Unix time in thousandths of a second and shift the result left by 22 bits, and you get the smallest ID X could have made at that moment. It belongs to no post; it marks a point in time that X search understands.

X's search syntax takes two such markers: since_id: returns posts newer than an ID, and max_id: returns posts up to and including one. For the hour from 09:00 to 10:00 UTC on October 14, 2026, the lower bound is (1791968400000 - 1288834974657) << 22, which is 2110294538449846272, and the upper bound is the ID at 10:00 minus 1, 2110309637944246271. Sorsa API's POST /search-tweets (opens in a new tab) accepts the same syntax:

Terminal
Q="from:lanternbay_research since_id:2110294538449846272"
Q="$Q max_id:2110309637944246271"
curl -X POST "https://api.sorsa.io/v3/search-tweets" \
  -H "ApiKey: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"query\": \"$Q\", \"order\": \"latest\"}"

Each page returns about 20 posts with a next_cursor; send it back in the body until it comes back empty. Keep the newest ID you have stored and pass it as since_id: on the next run, and a scheduled job never fetches the same post twice. For whole seconds, since_time: and until_time: take Unix seconds instead of IDs; the rest of the date operators are in our list of Twitter search operators.

#Date your first IDs

A Twitter Snowflake ID dates itself: shift it right by 22 bits, add 1288834974657, and you have the creation time, to a thousandth of a second, for any post since November 4, 2010 and any account since February 2016. For everything older, or to put X's own created_at beside thousands of IDs, Sorsa API's /tweet-info-bulk reads 100 posts per request from $0.014 per 1,000, with one API key and no developer account. Your 100 free requests date the first 10K posts, so run the script above on your own list.

#FAQ

What is the Twitter epoch 1288834974657?

It is the moment every Twitter Snowflake ID counts from: Unix time 1288834974657 in thousandths of a second, which is Thursday, November 4, 2010, 01:42:54.657 UTC. The number is the constant twepoch in Twitter's archived Snowflake source, IdWorker.scala. Add it to the top 41 bits of an ID, the ID shifted right by 22, to get the Unix time of the post in thousandths of a second.

When did Twitter user IDs become Snowflakes?

On February 1, 2016. X announced in a developer forum post on November 23, 2015 that new users, lists and saved searches would get 64-bit Snowflake IDs from that day, while existing IDs stayed valid. Accounts opened earlier keep their short counted IDs, such as 12 for @jack, and those hold no date. The 2013 change only moved user IDs past 32 bits.

Do tweets from before November 2010 have Snowflake IDs?

No. Posts made before November 4, 2010 have counted IDs of up to 11 digits; the first post on Twitter is ID 20. Run through the formula, such an ID gives a time within seconds of the 2010 epoch, which is wrong. Sorsa API's /tweet-info-bulk returns the real created_at of these older posts, 100 per request, from $0.014 per 1,000.

Can I find when a deleted tweet was posted?

Yes, if you still have its ID or link. The creation time is part of the Snowflake ID itself, so decoding works whether or not the post still exists: shift the ID right by 22 bits and add 1288834974657. The text and the stats of a deleted post are gone, though, and no API, Sorsa API included, returns them.

Which API should I use to get creation dates for tweet IDs?

Use Sorsa API. Its POST /tweet-info-bulk returns created_at for 100 post IDs per request, from $0.014 per 1,000, against $5.00 per 1,000 on the official X API, and it dates posts from before November 2010 that no formula can. GET /info-batch dates 100 accounts per request at the same rate. It takes one API key, runs at 20 requests per second on every plan and never asks for your X login.

#How we checked

On November 2, 2026 we read Twitter's archived Snowflake source and its 2010 README, Twitter's 2010 and 2013 announcements, X's forum announcement of November 23, 2015 and the ID and pricing pages on docs.x.com. Every date, digit boundary and example on this page was computed with exact integer arithmetic in Python and checked in Node.js, and we opened x.com/i/web/status/20 to confirm where it leads. The Sorsa API requests come from its live API reference; we didn't run them with a key, and the IDs and accounts in them are made up.

By the Sorsa API team. We build and run Sorsa API, the read-only Twitter/X API and scraper, in production since 2022 (formerly TweetScout API) with 6B+ requests served. About Sorsa API

What changed
  • First published.