Click on an ID to copy.
| Property | Value |
|---|---|
| Bit Length | 64 |
| Output Length | 13 chars |
| Encoding | Crockford base32 |
| Sortable | Yes |
| Timestamped | Yes |
| Monotonic | Yes |
| Crypto Random | Yes |
TSID (Time-Sorted ID) is a 64-bit, time-sortable unique identifier format designed to combine the compactness of Twitter's Snowflake with the simplicity and ergonomics of ULID. Created by Fabio Lima and implemented in the tsid-creator Java library (with community ports to many other languages), TSID addresses a common pain point in distributed systems: the need for identifiers that are short enough to fit in a standard bigint database column, sortable by creation time, and readable enough for humans to work with in logs, URLs, and support conversations. The result is a 13-character string encoded in Crockford's base32 that packs millisecond-precision timestamps and uniqueness bits into a single 64-bit integer.
The design philosophy behind TSID is that many real-world applications do not need the full 128 bits of a UUID or ULID. By staying within 64 bits, TSID fits natively in a bigint column in PostgreSQL, MySQL, SQL Server, and virtually every other relational database. Primary key indexes are half the size of their 128-bit counterparts, joins and lookups are faster, and the value can be stored and transmitted as a single machine word on 64-bit architectures. The Crockford base32 encoding then provides a human-friendly string representation on top of this compact binary foundation.
A TSID is a 64-bit signed integer (the most significant bit is effectively unused to keep the value positive in languages with signed 64-bit integers) whose bits are divided into two logical sections.
The upper 42 bits store a millisecond-precision timestamp relative to a configurable custom epoch. The default epoch in the reference implementation is January 1, 2020 (UTC), but developers can choose any epoch appropriate for their system. Forty-two bits of millisecond precision yield a range of approximately 139.5 years from the chosen epoch. For the default 2020 epoch, the timestamp space extends until roughly the year 2159, providing ample headroom for long-lived systems.
The lower 22 bits serve as a uniqueness component. How these 22 bits are used depends on the generator configuration. In the simplest mode, all 22 bits are filled with random data, producing up to 4,194,304 unique values per millisecond without any node coordination. In multi-node deployments, the 22 bits can be subdivided into a node identifier and a counter or random segment. A common configuration uses 10 bits for a node ID (supporting up to 1,024 nodes) and 12 bits for randomness or sequence (up to 4,096 values per millisecond per node). The exact split depends on the deployment topology and throughput requirements.
The generation algorithm reads the current system time in milliseconds, subtracts the configured epoch to produce the 42-bit timestamp, and obtains the uniqueness bits from a CSPRNG, a monotonic counter, or a combination of node ID and random/counter bits. The two components are combined via bit-shifting and bitwise OR into a single 64-bit integer, then encoded using Crockford's base32 alphabet (0-9, A-Z excluding I, L, O, and U), yielding exactly 13 characters.
Crockford's base32 encoding brings several practical benefits. It is case-insensitive by specification: 0A1B2C3D4E5F6 and 0a1b2c3d4e5f6 decode to the same value. It excludes visually ambiguous characters -- the letters I, L, O, and U are omitted because they are easily confused with digits 1, 0, and each other. And because the encoding uses only alphanumeric characters, TSID strings are inherently URL-safe with no percent-encoding required.
Because the timestamp bits occupy the most significant positions, TSID values sort correctly both as integers and as strings. Lexicographic comparison of the Crockford base32 strings produces the same ordering as numeric comparison of the underlying 64-bit integers, and both match chronological creation order. This three-way sort consistency makes TSID values efficient and predictable as database primary keys, partition keys, and sort keys.
Primary keys in relational databases. TSID is an excellent fit for systems that want time-sorted primary keys without the storage overhead of 128-bit identifiers. Stored as a bigint, a TSID column consumes 8 bytes per row -- half of what a UUID or ULID requires. Indexes are correspondingly smaller, and join performance improves because integer comparisons are among the fastest operations a database can perform. The 13-character string form is available whenever a human-readable representation is needed, such as in API responses or log messages.
REST API resource identifiers. Exposing raw auto-increment IDs in URLs leaks information about record counts and invites enumeration. Exposing UUIDs makes URLs long and unwieldy. TSID offers a middle ground: a 13-character, URL-safe, case-insensitive identifier that reveals only approximate creation time. The Crockford base32 encoding ensures the string can be used directly in URL paths and query parameters with no escaping, and its case-insensitivity means /resources/0A1B2C3D4E5F6 and /resources/0a1b2c3d4e5f6 resolve to the same record.
Distributed event and message ordering. In event-driven architectures, messages often need a unique identifier that doubles as a sort key. TSID provides both in a compact package. Producers on different nodes can generate TSIDs independently without coordination (using the random uniqueness mode) or with minimal coordination (using the node-ID mode), and consumers can sort incoming events by their TSID to reconstruct chronological order.
Mobile and embedded systems. Environments with constrained memory, storage, or bandwidth benefit from the 64-bit size. Transmitting an 8-byte TSID costs half as much as a 16-byte UUID, and indexing TSIDs in memory uses half the space. For IoT devices, mobile clients, or embedded databases like SQLite, this efficiency matters at scale.
TSID is most directly comparable to Snowflake, since both produce 64-bit, time-sorted integers. Snowflake requires pre-assigned worker and datacenter IDs, adding operational overhead for provisioning. TSID offers a random-only mode that eliminates this coordination, as well as a node-ID mode for deterministic partitioning. On the representation side, Snowflake IDs are typically exposed as raw numeric strings (e.g., 1541815603606036480), while TSID encodes to a shorter, more readable 13-character Crockford base32 string. For teams that want a Snowflake-like structure with a friendlier output format, TSID is a natural evolution.
Compared to ULID, TSID trades randomness and collision resistance for compactness. ULID is a 128-bit format with 80 bits of randomness encoded as a 26-character string. TSID is a 64-bit format with at most 22 bits of uniqueness encoded as 13 characters. ULID's larger random space makes collisions astronomically unlikely, while TSID's smaller random space means collisions become a consideration at very high throughput. For most applications, TSID's 4 million unique values per millisecond is more than sufficient, and the half-size storage footprint is a significant advantage. Choose ULID when collision resistance matters more than compactness; choose TSID when a bigint column and short strings are priorities.
Against UUID v7, TSID has the advantage of size and readability. UUID v7 is 128 bits rendered as a 36-character hyphenated string, while TSID is 64 bits rendered as 13 characters. UUID v7 benefits from broad ecosystem support -- nearly every database, ORM, and programming language has native UUID handling -- and its larger random component (approximately 62 bits) provides stronger collision resistance than TSID's 22 bits. If your infrastructure is already built around UUID columns and tooling, UUID v7 may be the path of least resistance. If you are designing a new system and want the smallest possible sorted identifier that fits in a bigint, TSID is the more efficient choice.
import { TsidFactory } from 'tsid-ts';
const factory = TsidFactory.builder()
.withRandomFn(() => Math.random())
.build();
const tsid = factory.generate();
console.log(tsid.toString());TSID (Time-Sorted Unique Identifier) is a 64-bit unique ID that combines a timestamp with a random or node-based component. It is designed to be a compact, time-ordered alternative to UUIDs, fitting neatly into a 64-bit integer column while still providing strong uniqueness guarantees in distributed systems.
TSID and Snowflake share a similar structure of embedding a timestamp in the most significant bits of a 64-bit integer. However, TSID offers more flexible configuration of its bit layout and does not require a centralized worker ID assignment. TSID also provides a compact string encoding by default, making it more portable across systems.
TSID uses Crockford Base32 encoding to represent the 64-bit value as a compact, human-readable string. This encoding is case-insensitive, avoids ambiguous characters like I, L, O, and U, and is URL-safe. The result is a clean, short string that is easy to copy, share, and type.
A TSID is 13 characters long when encoded in Crockford Base32. This is significantly shorter than a UUID (36 characters) or ULID (26 characters), making TSID one of the most compact time-sorted identifier formats available while still fitting in a standard 64-bit integer column.
Yes, TSID is an excellent choice for database primary keys. Its 64-bit size is natively supported by most databases as a BIGINT column, offering better storage efficiency and index performance than 128-bit UUIDs. The embedded timestamp ensures chronological ordering, which keeps B-tree indexes compact and write-friendly.
SCRU128 is a 128-bit, sortable, globally unique identifier encoded as a 25-digit case-insensitive Base36 string, inspired by ULID and KSUID.
Snowflake ID is a 64-bit time-sortable identifier originally designed by Twitter, combining a timestamp, worker ID, and sequence number for distributed uniqueness.
ULID (Universally Unique Lexicographically Sortable Identifier) is a type of identifier designed to be both globally unique and sortable in a lexicographically manner.
UUID v7 is a time-sortable UUID defined by RFC 9562, embedding a millisecond Unix timestamp with cryptographic randomness for global uniqueness.
© 2024 Carova Labs. All rights reserved