A ULID is a universally unique lexicographically sortable identifier. It is
- 128-bit compatible with
UUID - 1.21e+24 unique ULIDs per millisecond
- Lexicographically sortable!
- Canonically encoded as a 26 character string, as opposed to the 36 character UUID
- Uses Crockford's base32 for better efficiency and readability (5 bits per character)
- Case insensitive
- No special characters (URL safe)
- Monotonic sort order (correctly detects and handles the same millisecond)
In general the structure of a ULID is as follows:
01AN4Z07BY 79KA1307SR9X4MV3
|----------| |----------------|
Timestamp Randomness
48bits 80bits
For more information have a look at the original specification.
Use pip to install the library
$ pip install python-ulidto include Pydantic support install the optional dependency like so
$ pip install python-ulid[pydantic]Create a new ULID object from the current timestamp
>>> from ulid import ULID
>>> ULID()
ULID(01E75HZVW36EAZKMF1W7XNMSB4)or use one of the named constructors
>>> import time, datetime
>>> ULID.from_timestamp(time.time())
ULID(01E75J1MKKWMGG0N5MBHFMRC84)
>>> ULID.from_datetime(datetime.datetime.now())
ULID(01E75J2XBK390V2XRH44EHC10X)There are several options for encoding the ULID object
(e.g. string, hex, int, bytes, UUID):
>>> str(ulid)
'01BTGNYV6HRNK8K8VKZASZCFPE'
>>> ulid.hex
'015ea15f6cd1c56689a373fab3f63ece'
>>> int(ulid)
1820576928786795198723644692628913870
>>> bytes(ulid)
b'\x01^\xa1_l\xd1\xc5f\x89\xa3s\xfa\xb3\xf6>\xce'
>>> ulid.to_uuid()
UUID('015ea15f-6cd1-c566-89a3-73fab3f63ece')It is also possible to directly access the timestamp component of a ULID,
either in UNIX epoch or as datetime.datetime
>>> ulid.timestamp
1505945939.153
>>> ulid.datetime
datetime.datetime(2017, 9, 20, 22, 18, 59, 153000, tzinfo=datetime.timezone.utc)The ULID class can be directly used for the popular data validation library
Pydantic like so
from pydantic import BaseModel
from ulid import ULID
class Model(BaseModel):
ulid: ULID
model = Model(ulid="DX89370400440532013000") # OK
model = Model(ulid="not-a-ulid") # Raises ValidationErrorThis library by default supports the implementation for monotonic sort order suggested by the official ULID specification.
This means that ULID values generated in the same millisecond will have linear increasing randomness values. If r_1 and r_2 are the randomness values of two ULIDs with the same timestamp, then r_2 = r_1 + 1.
The package comes with a CLI interface that can be invoked either by the script name
ulid or as python module python -m ulid. The CLI allows you to generate, inspect
and convert ULIDs, e.g.
$ ulid build
01HASFKBN8SKZTSVVS03K5AMMS
$ ulid build --from-datetime=2023-09-23T10:20:30
01HB0J0F5GCKEXNSWVAD5PEAC1
$ ulid show 01HASFKBN8SKZTSVVS03K5AMMS
ULID: 01HASFKBN8SKZTSVVS03K5AMMS
Hex: 018ab2f9aea8ccffacef7900e6555299
Int: 2049395013039097460549394558635823769
Timestamp: 1695219822.248
Datetime: 2023-09-20 14:23:42.248000+00:00There are several flags to select specific output formats for the show command, e.g.
$ ulid show --datetime 01HASFKBN8SKZTSVVS03K5AMMS
2023-09-20 14:23:42.248000+00:00The special character - allows to read values from stdin so that they can be piped. E.g.
$ echo 01HASFKBN8SKZTSVVS03K5AMMS | ulid show --uuid -
018ab2f9-aea8-4cff-acef-7900e6555299
$ date --iso-8601 | python -m ulid build --from-datetime -
01HAT9PVR02T3S13XB48S7GEHEFor a full overview of flags for the build and show commands use the --help option
(e.g. ulid show --help).
Contributions are welcome! Feel free to create pull-requests for issues or feature requests. It might be worth creating an issue upfront to discuss the matter.