Generate UUID in Python
Python ships a uuid module in its standard library — no pip install, no dependencies. uuid.uuid4() gives you a CSPRNG-backed v4 UUID in one line.
Quick Reference
| Function | Version | Sortable | Use Case |
|---|---|---|---|
| uuid.uuid4() | v4 | No | General purpose — session IDs, API keys, record IDs |
| uuid.uuid1() | v1 | Yes | Legacy systems, Cassandra timeuuid |
| uuid.uuid5() | v5 | No | Deterministic — same input always gives same UUID |
| uuid.UUID(str) | any | — | Parse and validate an existing UUID string |
Primary Implementation
import uuid
# UUID v4 — random, CSPRNG-backed, zero dependencies
id = uuid.uuid4()
print(id)
# → f47ac10b-58cc-4372-a567-0e02b2c3d479
# As a plain string
id_str = str(uuid.uuid4())
# As a 32-char hex string (no hyphens)
id_hex = uuid.uuid4().hex
# → f47ac10b58cc4372a5670e02b2c3d479
# As bytes (16 bytes)
id_bytes = uuid.uuid4().bytes
# Generate multiple
ids = [str(uuid.uuid4()) for _ in range(5)]
# Parse and validate an existing UUID string
try:
parsed = uuid.UUID("f47ac10b-58cc-4372-a567-0e02b2c3d479")
print(parsed.version) # → 4
except ValueError:
print("Invalid UUID")
All UUID Versions
UUID v4 — Random (recommended default)
import uuid # 122 bits of randomness — use for anything that just needs a unique ID id = uuid.uuid4() print(str(id)) # → "550e8400-e29b-41d4-a716-446655440000"
UUID v5 — Deterministic / Namespace-based
import uuid # SHA-1 hash of namespace + name — same inputs always produce the same UUID # Useful for deduplication and content-addressable IDs id = uuid.uuid5(uuid.NAMESPACE_DNS, "example.com") print(str(id)) # → always "cfbff0d1-9375-5685-968c-48ce8b15ae17" # Custom namespace my_ns = uuid.uuid4() # generate once, store it record_id = uuid.uuid5(my_ns, "user:42")
UUID v1 — Timestamp + MAC (legacy)
import uuid # Encodes current timestamp + MAC address — sortable but leaks host info # Only use for Cassandra timeuuid or legacy system compatibility id = uuid.uuid1() print(id.time) # 100-nanosecond intervals since Oct 15, 1582
Real-World Use Cases
1. Django / SQLAlchemy model primary key
import uuid
from django.db import models
class Order(models.Model):
# Django natively supports UUIDField
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
created_at = models.DateTimeField(auto_now_add=True)
# SQLAlchemy equivalent
from sqlalchemy import Column, String
from sqlalchemy.dialects.postgresql import UUID as PG_UUID
class Product(Base):
__tablename__ = "products"
id = Column(PG_UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
2. FastAPI request tracing
import uuid
from fastapi import FastAPI, Request
import logging
app = FastAPI()
logger = logging.getLogger(__name__)
@app.middleware("http")
async def add_request_id(request: Request, call_next):
request_id = str(uuid.uuid4())
request.state.request_id = request_id
response = await call_next(request)
response.headers["X-Request-ID"] = request_id
logger.info(f"[{request_id}] {request.method} {request.url.path}")
return response
3. Deduplication with UUID v5
import uuid
# Idempotent event ID — same event always gets the same UUID
EVENT_NAMESPACE = uuid.UUID("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
def event_id(source: str, event_type: str, timestamp: str) -> str:
key = f"{source}:{event_type}:{timestamp}"
return str(uuid.uuid5(EVENT_NAMESPACE, key))
# Calling twice with same args returns the same UUID — safe to retry
id1 = event_id("payments", "charge.created", "2026-05-01T12:00:00Z")
id2 = event_id("payments", "charge.created", "2026-05-01T12:00:00Z")
assert id1 == id2 # True
Common Mistakes
Passing uuid.uuid4 instead of uuid.uuid4() as a default
In Django/SQLAlchemy, pass the function reference default=uuid.uuid4 (no parentheses). With parentheses, every row gets the same UUID generated at class definition time.
Storing as string when the DB supports a native UUID type
PostgreSQL has a native UUID column type that stores 16 bytes. Storing as VARCHAR(36) wastes 20 bytes per row and slows index lookups.
Using uuid.uuid1() in production web services
UUID v1 embeds your server's MAC address. This leaks network interface information and can be a privacy/security concern. Use v4 unless you specifically need Cassandra timeuuid.
How It Works
uuid.uuid4() calls os.urandom(16) internally to get 16 random bytes from the OS CSPRNG, then sets the version bits (bits 12–15 of octet 6 = 0100) and variant bits (bits 6–7 of octet 8 = 10).
The result is a uuid.UUID object. Call str() on it for the standard hyphenated format, .hex for 32-char no-hyphen, or .bytes for raw 16-byte binary.
The uuid.UUID object is immutable, hashable, and comparable — you can use it directly as a dict key or in a set.
Output Formats
str(id)
f47ac10b-58cc-4372-a567-0e02b2c3d479
id.hex
f47ac10b58cc4372a5670e02b2c3d479
id.bytes (16 bytes)
b'\xf4z\xc1\x0bX\xccCr\xa5g\x0e\x02\xb2\xc3\xd4y'
id.int
324716517733474852975027497220825710713
Best Practices
Use uuid.uuid4() as the default for all new code.
Pass the UUID object around — only convert to string at the boundary (DB write, API response).
Use uuid.uuid5() for deterministic IDs — great for idempotent event processing.
Performance
CPython generates roughly 1–3 million UUIDs/second on modern hardware. The bottleneck is os.urandom() — a syscall on every call.
For bulk generation, the uuid module is sufficient. If you need higher throughput, generate a large buffer with os.urandom(16 * n) and slice it manually.
Installation
# No installation needed — standard library
Available in Python 2.5+ and all Python 3.x versions. No pip install required.
Security
Entropy source: os.urandom() — maps to /dev/urandom on Linux/macOS and CryptGenRandom on Windows. Cryptographically secure.
Suitable for session tokens, CSRF tokens, and API keys. Do not use uuid.uuid1() for security-sensitive IDs.