K-Lab

ULID Decoder

Paste a ULID above to decode it

All processing happens in your browser. No data is sent to any server.

About this tool

How a ULID splits into timestamp and randomness

A ULID is 128 bits printed as exactly 26 characters with no hyphens, encoded in Crockford Base32. The split is positional and fixed: the first 10 characters carry a 48-bit count of milliseconds since the Unix epoch, and the remaining 16 characters carry 80 bits of randomness. Because Crockford Base32 assigns increasing values to characters in ascending order, comparing two ULIDs as ordinary strings produces the same ordering as comparing their timestamps, which is the property the format exists for. The 48-bit timestamp runs out in the year 10889, so the range is not a practical concern. The 80-bit random component is considerably larger than the 74 bits UUID v7 has available, which means two ULIDs generated in the same millisecond by unrelated processes are extremely unlikely to collide even at high volume. Crockford Base32 also excludes the letters I, L, O, and U, the first three because they are confusable with digits and the last to avoid accidental profanity, and decoders are expected to accept lowercase input and treat I and L as 1 and O as 0 when reading human-transcribed values.

ULID compared with UUID v7 and KSUID

All three formats solve the same problem, a unique identifier that sorts by creation time without a central sequence, and they differ mainly in encoding and in how they divide their bits. ULID and UUID v7 both carry 128 bits with a 48-bit millisecond timestamp; the difference is presentation and random capacity. ULID prints as 26 Crockford Base32 characters that are URL-safe and shorter than the 36 characters a hyphenated UUID needs, and it keeps 80 random bits. UUID v7 prints in the familiar 8-4-4-4-12 hexadecimal form, which costs ten more characters and leaves 74 random bits after the version and variant fields are subtracted, but it is a drop-in fit for every database UUID column, ORM, and library that already exists. KSUID takes a different trade: 160 bits total, a 32-bit timestamp with only second precision, and a 128-bit random payload printed as 27 Base62 characters. Second precision means KSUIDs generated within the same second are ordered arbitrarily among themselves, which is fine for event storage but wrong if you need to reconstruct sub-second ordering. Choose ULID when compact URL-safe text and sub-second ordering both matter, UUID v7 when ecosystem compatibility outweighs everything, and KSUID when a very large random payload matters more than millisecond ordering.

Frequently Asked Questions

Why is my ULID rejected as invalid?

There are four common causes and the decoder distinguishes between them. The first is length: a ULID is exactly 26 characters, so a 25 or 27 character string has been truncated or has picked up a stray character, and a 36 character string with hyphens is a UUID rather than a ULID. The second is the alphabet: Crockford Base32 deliberately omits I, L, O, and U, so a string containing any of those has either been mistyped or has been produced by something that is not a ULID encoder. Conforming decoders map I and L to 1 and O to 0 when reading, since those substitutions come from human transcription, but a U is unambiguously wrong. The third is overflow: the first character encodes only the top bits of the 48-bit timestamp, so it cannot exceed 7, and a ULID starting with 8 or higher describes a timestamp beyond the representable range. The fourth is invisible characters, usually a zero-width space or a non-breaking space picked up when copying from a web page or a chat client, which makes a visually correct ULID fail a length check.

Are ULIDs monotonic within a single millisecond?

Not by default, and this is the most common misunderstanding about the format. The base specification fills the random component with 80 fresh random bits on every call, so two ULIDs generated in the same millisecond share an identical timestamp prefix and are then ordered by whatever their random tails happen to be, which is effectively arbitrary. For most uses that is acceptable, since ordering within a millisecond rarely carries meaning. When it does matter, the specification describes an optional monotonic generator: within the same millisecond it takes the previous random value and increments it by one instead of drawing a new one, which guarantees that later calls sort after earlier ones. Most mature ULID libraries expose this as a separate monotonic factory rather than the default, so you have to opt in. Be aware that a monotonic generator holds state, meaning two processes generating ULIDs concurrently are monotonic only within themselves, and that the increment can overflow if a single process generates more than 2^80 IDs in one millisecond, which is not a realistic concern.

Can a ULID be converted to a UUID?

Yes, because both are 128-bit values and the conversion is a pure re-encoding of the same bits. Decode the 26 Crockford Base32 characters into 16 bytes and print those bytes as hexadecimal in the 8-4-4-4-12 grouping, and you have a UUID-shaped string that round-trips back to the original ULID. This is a common storage strategy: keep the value in a native UUID column so the database gets a compact 16-byte binary key with full index support, and present it as a ULID at the API boundary where the shorter URL-safe text is nicer. The caveat is that the result is not a valid UUID of any version. The bits that a UUID reader expects to find in the version and variant fields are, in a ULID, ordinary timestamp and randomness bits, so a strict UUID parser will report a nonsensical version and an unexpected variant. That is harmless as long as nothing downstream tries to interpret the value as a versioned UUID, but it means you cannot mix converted ULIDs and real UUIDs in a column where consumers branch on the version digit.

Standards & References

  • ULID Spec — Universally Unique Lexicographically Sortable Identifier
  • Crockford Base32 — The alphabet ULID uses, excluding I, L, O and U