ULID Generator

Generate lexicographically sortable unique IDs

Count

Click Generate to create ULIDs

What is a ULID?

ULID (Universally Unique Lexicographically Sortable Identifier) is a 128-bit identifier format designed to be both unique and naturally sortable. Unlike UUID v4, which is entirely random, a ULID embeds a millisecond-precision Unix timestamp in its high bits — ensuring that IDs generated later sort after IDs generated earlier.

ULIDs are encoded using Crockford's Base32 alphabet, producing a compact 26-character string that is URL-safe, case-insensitive, and free of visually ambiguous characters.

The ULID specification was created by Alizain Feerasta and is widely used in distributed systems, event sourcing, and database primary keys where both uniqueness and time-ordering matter. ULIDs are not an IETF standard — they are a community specification available at ulid.github.io.

ULID Structure

A ULID is 128 bits wide, divided into two components:

TimestampRandom
01ARZ3NDEKTSVE4RRFFQ69G5FAV
48 bits — Unix timestamp in milliseconds. Encodes the generation time with 1ms precision. Covers dates until the year 10889.80 bits — cryptographically secure random data. Regenerated fresh for each ULID (unless monotonicity mode is used).

Encoded as 26 Crockford Base32 characters: the first 10 characters represent the timestamp, the last 16 characters represent the random component.

Crockford Base32 Alphabet

ULIDs use Crockford's Base32 encoding, which uses 32 of the 36 alphanumeric characters. The alphabet is: 0123456789ABCDEFGHJKMNPQRSTVWXYZ

Why these 32 characters?
0123456789ABCDEFGHJKMNPQRSTVWXYZ

Four characters are intentionally excluded:

  • I and L are excluded — easily confused with the digit 1
  • O is excluded — easily confused with the digit 0
  • U is excluded — avoids accidental obscene words in generated IDs
  • The encoding is case-insensitive01ARZ3NDEKTSV4RRFFQ69G5FAV and 01arz3ndektsv4rrffq69g5fav are the same ULID

Crockford Base32 is more efficient than hexadecimal (32 symbols vs 16) and more human-readable than Base64 (no + / = characters, case-insensitive).

ULID vs UUID

ULIDs and UUIDs both represent 128-bit identifiers, but differ significantly in encoding and design goals:

PropertyULIDUUID
FormatCrockford Base32Hyphenated hex
Length26 characters36 characters
Timestamp48-bit Unix msNone (v4) or 48-bit ms (v7)
SortableYes — lexicographicNo (v4) / Yes (v7)
URL-safeYes (alphanumeric only)Yes (with hyphens)
DependenciesRequires libraryNative (crypto.randomUUID)
DB supportStore as CHAR(26) or BINARY(16)Native UUID column type
SpecCommunity spec (ulid.github.io)RFC 4122 / RFC 9562

If you are already in a UUID ecosystem (PostgreSQL uuid columns, REST APIs, ORMs with UUID support), UUID v7 is usually a better fit than ULID. If you prefer a more human-friendly encoding and control the full stack, ULID is an excellent choice.

ULID vs nanoid

Both ULID and nanoid produce short, URL-safe identifiers, but they have different design goals:

PropertyULIDNanoID
TimestampYes — 48-bit Unix msNo
SortableYesNo
Default length26 characters21 characters
Entropy80 bits (random component)~126 bits
AlphabetCrockford Base32 (32 chars)URL-safe Base64 (64 chars)
Customizable lengthNoYes (any length)
Use caseTime-ordered IDs, DB primary keysRandom tokens, short URLs, API keys

Choose ULID when you need time-ordering. Choose nanoid when you need maximum entropy in a short, random string.

Using ULIDs in Databases

ULIDs can be stored in databases in several ways depending on your requirements:

Store as CHAR(26)
The simplest approach. Lexicographic sort order is preserved. Slightly larger than binary storage but human-readable and easy to debug.
Store as BINARY(16)
Decode the ULID to its 16-byte binary representation for compact storage. Requires encoding/decoding in application code. Sort order is preserved.
PostgreSQL
Store as CHAR(26) or use the bytea type for binary storage. There is no native ULID type, but lexicographic ordering on CHAR(26) works correctly.
MySQL / MariaDB
Use CHAR(26) CHARACTER SET ascii for efficient storage. Binary storage in BINARY(16) also works and saves space.
SQLite
Store as TEXT. SQLite's default text comparison sorts ULIDs correctly due to their lexicographic design.
MongoDB
Store as a string field. ULID's lexicographic sortability works naturally with MongoDB's string comparison.

Code Examples

ULID generation requires the ulid library (available for JavaScript, Python, Go, Rust, and more):

JavaScript (ulid npm)
// Using the 'ulid' npm package
import { ulid } from 'ulid'

const id = ulid()          // "01ARZ3NDEKTSV4RRFFQ69G5FAV"
const id2 = ulid()         // "01ARZ3NDEKXXXXXXXXXXXX..." (same ms, incremented random)

// With a custom timestamp
const id3 = ulid(1469918176385) // deterministic time portion

// Extract the timestamp back out
import { decodeTime } from 'ulid'
decodeTime(id)  // → 1469918176385 (Unix ms)
Python (python-ulid)
# Using python-ulid
from ulid import ULID

uid = ULID()
str(uid)                    # "01ARZ3NDEKTSV4RRFFQ69G5FAV"
uid.timestamp               # 1469918176.385
uid.datetime                # datetime(2016, 7, 30, 23, 36, 16, 385000, tzinfo=timezone.utc)

# Parse an existing ULID string
parsed = ULID.from_str("01ARZ3NDEKTSV4RRFFQ69G5FAV")
parsed.timestamp            # 1469918176.385
PostgreSQL
-- Store ULIDs as TEXT or CHAR(26)
CREATE TABLE events (
  id   CHAR(26) PRIMARY KEY DEFAULT gen_ulid(),  -- if using a PG extension
  name TEXT NOT NULL,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- Or pass the ULID from your application layer
INSERT INTO events (id, name, created_at)
VALUES ('01ARZ3NDEKTSV4RRFFQ69G5FAV', 'user.signup', NOW());

-- Range queries are efficient because ULIDs sort chronologically
SELECT * FROM events
WHERE id > '01ARZ3NDEKTSV4RRFFQ69G5FAV'
ORDER BY id
LIMIT 20;
JavaScript (pure — no dependencies)
// Pure browser / Deno / Node.js implementation (no dependencies)
const CROCKFORD = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'

function encodeTime(ms) {
  let result = '', n = BigInt(ms)
  for (let i = 9; i >= 0; i--) {
    result = CROCKFORD[Number(n & 31n)] + result
    n >>= 5n
  }
  return result
}

function encodeRandom(bytes) {
  let n = 0n
  for (const b of bytes) n = (n << 8n) | BigInt(b)
  let result = ''
  for (let i = 15; i >= 0; i--) {
    result = CROCKFORD[Number(n & 31n)] + result
    n >>= 5n
  }
  return result
}

function generateULID() {
  const randomBytes = new Uint8Array(10)
  crypto.getRandomValues(randomBytes)
  return encodeTime(Date.now()) + encodeRandom(randomBytes)
}

Frequently Asked Questions

Are ULIDs globally unique?
Yes — with very high probability. The 80-bit random component provides 2^80 ≈ 1.2 × 10^24 possible values per millisecond per generator. The probability of a collision within the same millisecond across different generators is negligible for any practical system. For strict uniqueness guarantees, use monotonic mode, which increments the random component for IDs generated within the same millisecond.
Is a ULID the same as a UUID?
No — ULIDs and UUIDs are different encodings of a 128-bit value. A ULID cannot be directly substituted for a UUID in systems that validate UUID format (hyphenated hex). However, you can convert between binary ULID and UUID representations if needed.
How does ULID monotonic mode work?
In standard mode, the 80-bit random component is freshly generated for each ULID. In monotonic mode, when multiple ULIDs are generated within the same millisecond, the random component of the second and subsequent IDs is the previous random value plus one. This ensures strict monotonic ordering within a millisecond on a single generator.
Can I decode a ULID to get the generation timestamp?
Yes. The first 10 Crockford Base32 characters encode the 48-bit Unix millisecond timestamp. Decode those characters using the Crockford Base32 alphabet, interpret the result as a Unix timestamp in milliseconds, and convert to a date. This tool does exactly that.
Is ULID an official standard?
No. ULID is a community specification maintained at ulid.github.io. It is not an IETF standard like UUID. This means there is no official RFC to reference, and implementations may vary slightly. UUID v7 (RFC 9562, 2024) is the IETF-standardised alternative with similar time-ordering properties.