DevKitHub

API & Security

ULID Generator & Decoder

Generate up to 1,000 ULIDs at once, or paste one to see when it was made. Values come from your browser’s cryptographic random source and clock, not from a server.

Generate

0 lines

Decoded

ULIDcanonical
01ARYZ6S41TSV4RRFFQ69G5FAV
Timestamp (ms)first 48 bits
1469918176385
Time (UTC)
2016-07-30T22:36:16.385Z
Randomness80 bits, hex
d6764c61efb99302bd5b
As UUIDnot a real version
01563df3-6481-d676-4c61-efb99302bd5b

The same 128 bits in UUID notation. It is not an RFC 9562 UUID: the version digit (d) and variant digit (4) are ordinary ULID bits, not a declared version. PostgreSQL’s uuid type stores it as it is; a validator that checks those digits may reject it.

This tool runs entirely in your browser. Your input is never uploaded, stored or logged.

How it works

A ULID is 128 bits: a 48-bit Unix timestamp in milliseconds followed by 80 random bits, written as 26 characters of Crockford’s Base32 — ten for the time and sixteen for the randomness. Because the time comes first and the alphabet is in ASCII order, sorting ULIDs as plain strings sorts them by creation time. That is the reason to choose one over a random UUID: new keys land at the end of a B-tree index instead of scattering across it. Decoding runs the other way, so the first ten characters of any ULID give the millisecond it was made — 01ARYZ6S41TSV4RRFFQ69G5FAV, the example from the reference implementation, dates from 22:36:16.385 UTC on 30 July 2016.

Two ULIDs made in the same millisecond share their first ten characters, and their random parts sort in no particular order. Monotonic mode, which the specification describes and which is on by default here, fixes that by taking the previous random part and adding one, so a batch made within a single millisecond still sorts in the order it was generated. The cost is that consecutive IDs can be predicted from each other, so turn it off when a ULID doubles as an unguessable token. If the random part is already at its maximum, the specification says generation must fail rather than wrap round, and it does. The random bits come from crypto.getRandomValues, never Math.random.

Decoding follows the specification, including the parts that are easy to get wrong. It is case-insensitive, but I, L, O and U are rejected rather than read as 1, 1 and 0. Crockford’s Base32 permits those aliases; the ULID specification defines only the alphabet, which leaves them out, and the reference implementation’s validator refuses them, so accepting them would pass strings that libraries will not parse. The error gives the position and the character that was probably meant. Twenty-six Base32 characters could hold 130 bits, so the first must be 0 to 7, and anything above 7ZZZZZZZZZZZZZZZZZZZZZZZZZ is refused. The UUID form is the same 128 bits as hex. It fits a PostgreSQL uuid column but is not an RFC 9562 UUID, because its version and variant digits are ordinary ULID bits. A UUID can be pasted in too: for version 7, whose first 48 bits are also Unix milliseconds, the decoded time is real.

Common problems

Every example below is run against this tool in our test suite, so what it says here is what the tool actually does.

Character 26: “O” is not in the ULID alphabet: Crockford’s Base32 leaves out I, L, O and U.

01ARYZ6S41TSV4RRFFQ69G5FAO
Why:
A letter O typed or read where a zero belongs, or an I or L for a one. The alphabet leaves those letters out because they are confused with digits, and ULID libraries reject them.
Fix:
Replace it with 0, or with 1 for I and L. The error gives the position.

The first character is 8, which makes the value larger than 128 bits.

8ZZZZZZZZZZZZZZZZZZZZZZZZZ
Why:
Twenty-six Base32 characters hold 130 bits and a ULID has 128, so only 0 to 7 can come first. A higher first character is usually a hand-made test value, or a string that was never a ULID.
Fix:
Check where the value came from. The largest valid ULID is 7ZZZZZZZZZZZZZZZZZZZZZZZZZ, in the year 10889.

A ULID is 26 characters long; this is 25.

01ARYZ6S41TSV4RRFFQ69G5FA
Why:
A character lost in copying — usually the last one, dropped by a selection that stopped short or a column that truncated it.
Fix:
Copy the whole value again. A ULID is always exactly 26 characters.

IDs made in the same millisecond come out of order.

Why:
Monotonic mode was off, or the IDs came from different processes. Within one millisecond only the random part differs, and random values sort randomly.
Fix:
Generate from one monotonic generator where the order matters, and do not rely on millisecond ordering between machines.

The next ID could be guessed from the previous one.

Why:
Monotonic mode adds one to the random part within a millisecond, so the ID after 01BX5ZZKBKACTAV9WEVGEMMVRZ is 01BX5ZZKBKACTAV9WEVGEMMVS0 — the specification’s own example.
Fix:
Do not use monotonic ULIDs as secrets. Turn monotonic mode off, or use a separate random token.

Frequently asked questions

What is the difference between a ULID and a UUID?
Both are 128 bits. A version 4 UUID is 122 random bits and sorts in no useful order; a ULID puts a millisecond timestamp in its first 48 bits, so ULIDs sort by creation time, and it is written as 26 characters instead of 36. UUIDv7 brings the same idea into the UUID standard, with the same 48-bit timestamp at the front.
How do I get the timestamp from a ULID?
The first ten characters are the Unix time in milliseconds, in Base32. Paste the ULID above and the Timestamp (ms) row shows it, with the UTC time below. 01ARYZ6S41TSV4RRFFQ69G5FAV decodes to 1469918176385, which is 30 July 2016.
Can I store a ULID in a UUID column?
Yes. It is the same 128 bits, and the As UUID row gives the hex form a PostgreSQL uuid column accepts. It is not a standards-conforming UUID, though — its version and variant digits are ordinary ULID bits — so code that validates UUID versions may reject it.
Are the ULIDs generated on your server?
No. They are made in your browser from its clock and crypto.getRandomValues. Nothing is transmitted, stored or logged.

Last updated