Create KSUID identifiers instantly in your browser—ideal for sortable unique IDs in databases, APIs, and event logs.
KSUID = 4-byte timestamp (since 2014-05-13) + 16-byte randomness, encoded as 27-char base62. Sort order matches generation time.
KSUIDs (K-Sortable Unique IDentifiers) are 27-character base62 strings that combine a 32-bit UTC timestamp with 128 bits of cryptographic randomness, so they sort by creation time under any byte-wise comparison without giving up the coordination-free generation of UUIDs. Originally created by Segment for ordering log events archived to S3, KSUIDs are now a popular choice for distributed event IDs, message queue keys, and audit log entries. XConvert's free KSUID Generator produces spec-compliant KSUIDs entirely in your browser — no server roundtrip, no logging, no sign-up.
crypto.getRandomValues() Web Crypto CSPRNG.0-9A-Za-z. The first 9 or so characters carry the timestamp and the remaining ~18 carry the random payload — you can decode either part with any segmentio/ksuid library port or the ksuid CLI..txt file with one KSUID per line for pasting into SQL INSERT statements, seed scripts, or test fixtures.A KSUID is a 160-bit identifier specified by the segmentio/ksuid reference implementation, originally published by Segment (now part of Twilio) in 2017. Its layout is straightforward: 4 bytes of big-endian UTC timestamp followed by 16 bytes of randomness, encoded as a 27-character string using the base62 alphabet 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz. That alphabet ordering is deliberate — under Unicode codepoint ordering, digits sort before uppercase which sorts before lowercase, so byte-wise comparison of the encoded string matches the integer comparison of the underlying value.
Because the timestamp lives in the high-order bits, KSUIDs sort the same way as their creation order under any byte-wise, string, or memcmp comparison. That property is what made them appealing to Segment's pipeline team: they could archive event payloads to S3 keyed by KSUID and the resulting object listing was already in time order without a separate sort step. The 128 bits of randomness give roughly 3.4 × 10^38 possible values per second of timestamp — 64 times larger than UUID v4's 122-bit keyspace, per the README — so collisions within a single second on a single node are statistically negligible.
The epoch is the choice that distinguishes KSUID from a naive Unix-timestamp design. Using May 13, 2014 (1400000000 in Unix seconds) instead of January 1, 1970 reclaims about 44 years of unused timestamp range from the 32-bit field. With the adjusted epoch, KSUIDs can be generated through approximately the year 2150 before the 32-bit timestamp overflows — vs only year 2106 if Unix epoch were used directly.
| Property | KSUID | UUID v7 | ULID | Snowflake |
|---|---|---|---|---|
| Total bits | 160 | 128 | 128 | 64 |
| String length | 27 chars | 36 chars | 26 chars | up to 19 digits |
| Encoding | Base62 | Hex with hyphens | Crockford Base32 | Decimal |
| Timestamp bits | 32 (seconds) | 48 (ms) | 48 (ms) | 41 (ms) |
| Random/sequence bits | 128 | ~74 (after version/variant) | 80 | 22 (10 worker + 12 seq) |
| Timestamp epoch | 2014-05-13 UTC | Unix (1970) | Unix (1970) | Custom (Twitter: 2010) |
| Resolution | 1 second | 1 millisecond | 1 millisecond | 1 millisecond |
| Lexicographically sortable | Yes | Yes | Yes | Yes (numeric) |
Fits 128-bit uuid column |
No (160 bits, needs CHAR(27) or BINARY(20)) | Yes | Yes | No (fits BIGINT) |
| Standardized by | segmentio/ksuid (2017) | RFC 9562 (May 2024) | github.com/ulid/spec | Twitter (2010) |
| Coordination-free | Yes | Yes | Yes | No (needs worker ID) |
| Maximum representable date | ~year 2150 | ~year 10889 | ~year 10889 | ~year 2080 (twepoch-dependent) |
| Bytes | Width | Field | Meaning |
|---|---|---|---|
| 0–3 | 4 bytes (32 bits) | Timestamp | Big-endian unsigned int, UTC seconds since May 13, 2014 (1400000000 Unix epoch) |
| 4–19 | 16 bytes (128 bits) | Payload | Cryptographically random, sourced from the platform CSPRNG |
| Total | 20 bytes | — | Encoded as 27 characters in base62 (0-9A-Za-z) |
The text representation is always exactly 27 characters — the minimum needed because ceil(log62(2^160)) = 27. The lowest possible string is 000000000000000000000000000 (all zeros) and the highest is aWgEPTl1tmebfsQzFP4bxwgy80V per the reference Go implementation.
Event-stream and message-queue keys — Use KSUIDs as event IDs in Kafka, Kinesis, SQS, or any outbox table. The embedded timestamp gives consumers a natural ordering key, and the 128 bits of randomness make collisions across producers statistically impossible without coordination. Segment originally built KSUID to key S3-archived event payloads exactly for this reason.
Distributed database primary keys — KSUIDs work as primary keys in tables with high write volume because new inserts cluster at the end of a B-tree index instead of scattering randomly like UUID v4. Store them as CHAR(27) for the human-readable string or BINARY(20) for the compact binary form. Note: unlike UUID v7 and ULID, a KSUID does not fit in a 128-bit uuid column — pick UUID v7 if 128-bit storage compatibility is a hard requirement.
Audit logs and event sourcing — Assign a KSUID to each audit record or event in an event store. The 1-second timestamp resolution is plenty for human-readable audit trails, and queries like "everything between two timestamps" can be answered by string range alone without a separate created_at index.
Distributed tracing and correlation IDs — Use KSUIDs as request IDs across microservices. The timestamp prefix lets ops engineers eyeball the age of an ID in a log line without parsing it, and lexicographic sort makes log aggregators sort trace events correctly even when clocks drift slightly between nodes.
Cursor-based pagination — When you need a stable, sort-friendly cursor for paginating API results, KSUIDs work better than offset pagination (which breaks under concurrent inserts) and better than created_at + id composite cursors (which need two columns). Pass the last seen KSUID as the ?after= cursor; the next page is WHERE id > :cursor ORDER BY id LIMIT n.
Test fixtures and load-test payloads — Generate batches of KSUIDs for integration tests, k6/JMeter scripts, or development database seeds. The independent randomness across IDs means tests can run in any order without cross-contamination, and the embedded timestamp makes it trivial to filter fixtures by "this test run only."
XConvert's KSUID Generator follows the segmentio/ksuid reference layout exactly: 4 bytes of UTC timestamp (big-endian, seconds since epochStamp = 1400000000) followed by 16 bytes of randomness from the browser's crypto.getRandomValues() Web Crypto CSPRNG, then the 20-byte big-endian payload encoded to 27 characters using the base62 alphabet 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.
All randomness is sourced from the Web Crypto API's crypto.getRandomValues(), which is backed by the operating system CSPRNG (/dev/urandom on Linux/macOS, BCryptGenRandom on Windows, getentropy on iOS). Bulk generation issues a single getRandomValues() call sized to cover every KSUID in the batch, then slices the buffer in 16-byte chunks per KSUID — measurably faster than calling the API once per ID. Generation, encoding, and rendering all happen client-side; your KSUIDs are never sent to any server, which matters when they are used as session tokens, idempotency keys, or other secrets.
Unlike ULID, the KSUID spec does not define a monotonic mode for the same-tick case. Two KSUIDs generated in the same second get fully independent random tails, so they sort in random order within that second. In practice this is rarely an issue — second-resolution buckets are coarse enough that "sub-second order within a bucket" is acceptable for most pipelines. If you need millisecond-resolution ordering or a defined monotonic mode, use ULID or UUID v7 instead.
The Unix epoch (January 1, 1970) is too far in the past to be useful for a 32-bit second-resolution timestamp: 2^32 seconds is roughly 136 years, so a 32-bit Unix-second timestamp overflows in 2106. By shifting the epoch forward by 1400000000 seconds (May 13, 2014, 16:53:20 UTC), KSUIDs reclaim 44 years of unused range and remain valid through approximately the year 2150. Segment's reference Go implementation defines this as epochStamp int64 = 1400000000. Note: the README text says "May 13th, 2014" while issue #40 reports that some earlier README revisions said "March 5th, 2014" by mistake — May 13 is what the code actually uses.
A 160-bit value encoded in base62 (about 5.95 bits per character) needs ceil(160 / log2(62)) = 27 characters — no padding, no hyphens, no version bits to spare. UUID's 36-character canonical form (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) is hex (4 bits per character) with four delimiter hyphens, so a 128-bit UUID costs 32 hex characters plus 4 hyphens. KSUID is denser per bit (base62 packs more bits per character than hex) and skips delimiters entirely, which makes it copy-paste-safe and never breaks when an IDE auto-wraps a long line.
0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz — digits first, then uppercase, then lowercase. The ordering is deliberate: under Unicode/ASCII codepoint ordering, 0 (0x30) < A (0x41) < a (0x61), so a byte-wise string comparison of two KSUIDs matches the integer comparison of the underlying 160-bit value. The encoding is case-sensitive — aWgEPTl1... is not equivalent to AWGEPTL1..., unlike Crockford Base32 (used by ULID) which is case-insensitive by design.
Yes — that is the entire point of the "K-Sortable" name. The 32-bit timestamp lives in the high-order bits, and the base62 alphabet is ordered so that lexicographic byte-wise comparison matches numeric comparison. You can ORDER BY ksuid_column in PostgreSQL or MySQL without any cast or function call and get rows in creation order. The same applies to listing S3 objects keyed by KSUID, sorting log lines that contain KSUIDs, or any other context where strings are compared byte-by-byte.
One second. The 32-bit timestamp counts whole seconds since the KSUID epoch (May 13, 2014). Two KSUIDs generated in the same second share the same 4-byte timestamp prefix and sort by their independent 16-byte random tails, which is effectively random within that second. If your use case requires sub-second ordering, switch to ULID (millisecond resolution) or UUID v7 (also millisecond, plus an optional monotonic mode in RFC 9562).
No. The segmentio/ksuid spec does not define monotonic generation for the same-tick case — when two KSUIDs are produced in the same second, their order within that second is random. ULID's spec mandates monotonic generation as an option (increment the random tail instead of regenerating it within the same millisecond), and UUID v7 / RFC 9562 describes a similar "method 1" monotonic counter scheme. If guaranteed ordering inside a single tick matters, prefer ULID or UUID v7.
Yes. The 128-bit random payload is generated via the browser's Web Crypto API crypto.getRandomValues(), which MDN describes as producing "cryptographically strong random values" and which is backed by the operating system's CSPRNG (/dev/urandom on Linux/macOS, BCryptGenRandom on Windows). With 128 bits of randomness per KSUID, the keyspace is 64 times larger than UUID v4's 122-bit space, so collisions within a single second on a single node are a physical-impossibility-class event, not something you need to defend against in application code.
No. Generation happens entirely in your browser using crypto.getRandomValues(). The KSUIDs never leave the page, are not logged, and are not transmitted to XConvert or any third party. This matters because KSUIDs are often used as idempotency keys, session tokens, or other values that must remain confidential.
Yes. Decode the first 9 characters from base62 to recover the 32-bit big-endian timestamp value, then add 1400000000 to convert from the KSUID epoch back to a Unix timestamp. The segmentio/ksuid CLI (ksuid -f inspect <ksuid>) does this directly: it prints the timestamp, payload, and stringified form. Library ports for Go, Java (f4b6a3/ksuid-creator), Ruby, and other languages expose equivalent helpers. You can also feed the recovered Unix timestamp through XConvert's timestamp-to-date converter for a human-readable date.
Pick KSUID if you want a 27-character base62 string with the largest random payload of the three (128 bits vs ULID's 80 and UUID v7's ~74) and you are comfortable with second-resolution timestamps. Pick UUID v7 if you need to drop the ID into a 128-bit uuid column or interoperate with the broad UUID tooling ecosystem (RFC 9562, May 2024). Pick ULID (see the ulid/spec) if you want millisecond resolution and a defined monotonic mode, or if Crockford Base32 (case-insensitive, no lookalikes) fits your UX better than base62. All three are coordination-free; the choice is mostly about storage, resolution, and ecosystem fit.