Generating UUIDs in Python
On this page
Python has had a built-in uuid module since 2.5 (~2006). It covers v1, v3, v4, and v5 out of the box. v7 arrived in the standard library in Python 3.13 (October 2024); for earlier interpreters you need a third-party package.
The standard library
import uuid
uuid.uuid4() # random — the default
# UUID('12fbc73a-9e9d-44b1-9e6d-1d3e7c8a0f31')
uuid.uuid1() # time + node
uuid.uuid3(uuid.NAMESPACE_DNS, "example.com") # MD5 of namespace + name
uuid.uuid5(uuid.NAMESPACE_URL, "https://x.io") # SHA-1 of namespace + name
uuid.UUID is the type. It’s hashable and comparable, and it serializes cleanly:
u = uuid.uuid4()
str(u) # "12fbc73a-9e9d-44b1-9e6d-1d3e7c8a0f31"
u.hex # "12fbc73a9e9d44b19e6d1d3e7c8a0f31"
u.bytes # b'\x12\xfb\xc7:\x9e\x9dD\xb1\x9emN>...'
u.int # 25190934987130523... (single 128-bit int)
u.version # 4
u.variant # 'specified in RFC 4122'
v7 (Python 3.13+)
# Python 3.13 and newer
import uuid
uuid.uuid7()
# UUID('01928a47-3b30-7c5e-9d1a-f0b8c4a7e923')
For older Pythons, install uuid-utils — a Rust-backed library that adds v6, v7, and v8:
# pip install uuid-utils
import uuid_utils
uuid_utils.uuid7()
# UUID('01928a47-3b30-7c5e-9d1a-f0b8c4a7e923')
uuid_utils.UUID is a drop-in for the standard library type, so str(), .hex, .bytes, etc. all work the same way.
Parsing UUIDs from strings
u = uuid.UUID("12fbc73a-9e9d-44b1-9e6d-1d3e7c8a0f31") # canonical
u = uuid.UUID("12fbc73a9e9d44b19e6d1d3e7c8a0f31") # no hyphens
u = uuid.UUID("{12fbc73a-9e9d-44b1-9e6d-1d3e7c8a0f31}") # braces
u = uuid.UUID(bytes=b'\x12\xfb...') # 16 raw bytes
u = uuid.UUID(int=25190934987130523...) # int
The constructor accepts every form. Invalid strings raise ValueError — use a try/except for user-supplied input.
Validation
The Pythonic way is to let the constructor do it:
def is_uuid(s: str) -> bool:
try:
uuid.UUID(s)
return True
except ValueError:
return False
For stricter checks (e.g. require canonical hyphenated form):
def is_canonical_uuid(s: str) -> bool:
try:
return str(uuid.UUID(s)) == s.lower()
except ValueError:
return False
Cryptographic randomness
Python’s uuid.uuid4() uses os.urandom() under the hood, which is the platform’s CSPRNG (getrandom(2) on Linux, BCryptGenRandom on Windows). It’s safe to use as a session token or password reset URL component on any modern OS — though see collision probability for limits.
Database integration
PostgreSQL with psycopg
import psycopg
import uuid
with psycopg.connect("postgresql://...") as conn:
cur = conn.cursor()
cur.execute(
"INSERT INTO users (id, email) VALUES (%s, %s)",
(uuid.uuid4(), "alice@example.com"),
)
psycopg v3 maps Python uuid.UUID ↔ PostgreSQL uuid automatically. Older psycopg2 needs register_uuid().
SQLAlchemy
from sqlalchemy import Column
from sqlalchemy.dialects.postgresql import UUID
import uuid
class User(Base):
__tablename__ = "users"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
For a v7 default, swap in default=uuid.uuid7 (3.13+) or uuid_utils.uuid7.
Django
# models.py
from django.db import models
import uuid
class Order(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
For Django REST Framework, UUIDField works in serializers without configuration.
Performance
The standard library’s uuid4() is fine for most apps — typical throughput is 200,000–500,000 UUIDs/second on a modern machine. If you need more, uuid_utils (Rust) is ~5× faster. For batch generation in a tight loop, generate from secrets.token_bytes(16) and set the version/variant bits manually.
Common pitfalls
- Don’t store as
str. Use the database’s nativeuuidtype (PostgreSQL, MS SQL Server, MySQL 8+) — it’s half the storage and indexed faster. - Don’t mix versions in one column unless you have a reason. Pick v4 or v7 and stick with it.
uuid1()leaks node identity by default. Modern CPython randomizes the node ID so this is mostly historical, but if you parse third-party v1 UUIDs the embedded MAC may be real.- Truncated UUIDs aren’t UUIDs.
str(uuid.uuid4())[:8]is 32 random bits, not a UUID. Use nanoid or short-id libraries if you want short.
Cheat sheet
| Goal | Code |
|---|---|
| Random UUID | uuid.uuid4() |
| Time-ordered (DB key) | uuid.uuid7() (3.13+) or uuid_utils.uuid7() |
| Deterministic from name | uuid.uuid5(ns, name) |
| Parse | uuid.UUID(s) |
| Bytes for storage | u.bytes |
| 32-char hex | u.hex |
| Validate | try uuid.UUID(s) |
Try the tools
- UUID generator — pick a version, copy the output
- UUID v7 generator — for new database keys
- UUID validator — paste any UUID, get version + timestamp back