Nanosecond-Precision Universally Lexicographically Sortable Identifier (NULID) for Python
Python bindings for the nulid Rust crate, powered by PyO3.
A NULID is a 128-bit identifier with:
- 68-bit nanosecond timestamp for precise chronological ordering
- 60-bit cryptographically secure randomness for collision resistance
- 26-character Crockford Base32 encoding that is URL-safe and lexicographically sortable
- UUID-compatible size (16 bytes)
pip install nulidA Rust toolchain (1.88+) is required to build from source. Pre-built wheels are available for common platforms.
import nulid
# As a 26-character Base32 string
id = nulid.generate()
# => "01AN4Z07BY79K47PAZ7R9SZK18"
# As a 16-byte binary
raw = nulid.generate_bytes()
# => b'\x01\xan...'raw = nulid.generate_bytes()
# Bytes -> String
encoded = nulid.encode(raw)
# String -> Bytes
decoded = nulid.decode(encoded)
assert raw == decodedraw = nulid.generate_bytes()
ns = nulid.nanos(raw) # nanoseconds since epoch
ms = nulid.millis(raw) # milliseconds since epoch
rand = nulid.random(raw) # 60-bit random value
assert not nulid.is_nil(raw)
assert nulid.is_nil(b"\x00" * 16)For guaranteed strictly increasing IDs, even within the same nanosecond:
gen = nulid.Generator()
id1 = gen.generate()
id2 = gen.generate()
id3 = gen.generate()
assert id1 < id2 < id3Binary output:
gen = nulid.Generator()
b1 = gen.generate_bytes()
b2 = gen.generate_bytes()
assert b1 < b2For multi-node deployments, assign each node a unique ID (0-65535):
gen = nulid.DistributedGenerator(node_id=1)
id = gen.generate()
assert gen.node_id == 1The node ID is embedded in the random bits, guaranteeing cross-node uniqueness even with identical timestamps.
| Feature | ULID | NULID |
|---|---|---|
| Total Bits | 128 | 128 |
| String Length | 26 chars | 26 chars |
| Timestamp Bits | 48 (milliseconds) | 68 (nanoseconds) |
| Randomness Bits | 80 | 60 |
| Time Precision | 1 millisecond | 1 nanosecond |
| Lifespan | Until 10889 AD | Until ~11326 AD |
NULID trades 20 bits of randomness for 20 extra bits of timestamp precision, giving nanosecond-level ordering while still providing 1.15 quintillion unique IDs per nanosecond.
# Install in development mode
make develop
# Run all CI checks (format, clippy, lint, test)
make ci
# Run tests only
make test
# Format code (Python + Rust)
make fmt
# Show all available targets
make helpMIT - see LICENSE for details.