How to generate a UUID in Python

Python generates UUIDs with the built-in uuid module — no third-party package needed: uuid.uuid4() returns a random UUID in one line. All snippets below were run on Python 3.13.

The one-liner (UUID v4)

import uuid

my_id = uuid.uuid4()
print(my_id)        # 65e82210-1a2d-4011-a064-a6a60565aa41
print(str(my_id))   # same, as str
print(my_id.hex)    # 65e822101a2d4011a064a6a60565aa41 (no hyphens)

uuid4() is random (122 bits from os.urandom), cryptographically secure, and the right default for most use cases.

Every UUID version in Python

VersionCodeNotes
v4 (random)uuid.uuid4()Default choice
v7 (time-ordered)uuid.uuid7()Python 3.14+ (see below for older versions)
v1 (timestamp+node)uuid.uuid1()Leaks MAC address by default — avoid for new code
v5 (SHA-1 name-based)uuid.uuid5(uuid.NAMESPACE_DNS, "example.com")Deterministic — always cfbff0d1-9375-5685-…
v3 (MD5 name-based)uuid.uuid3(uuid.NAMESPACE_DNS, "example.com")Prefer v5 unless compatibility requires v3

UUID v7 on Python 3.13 and older

uuid.uuid7() landed in Python 3.14. On earlier versions, use the uuid6 package:

pip install uuid6

from uuid6 import uuid7
print(uuid7())   # 0198c2f1-6d8a-7cc3-...  (sortable by creation time)

Why bother? v7 IDs sort by creation time, which keeps database indexes compact — see our UUID v4 vs v7 comparison for the full reasoning.

Parsing and validating

import uuid

def is_valid_uuid(value: str) -> bool:
    try:
        uuid.UUID(value)
        return True
    except ValueError:
        return False

is_valid_uuid("550e8400-e29b-41d4-a716-446655440000")  # True
is_valid_uuid("not-a-uuid")                            # False

u = uuid.UUID("550e8400-e29b-41d4-a716-446655440000")
u.version   # 4
u.bytes     # 16 raw bytes — store this, not the 36-char string

UUID primary keys in Django and SQLAlchemy

# Django
import uuid
from django.db import models

class Order(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)

# SQLAlchemy 2.x
import uuid
from sqlalchemy.orm import Mapped, mapped_column

class Order(Base):
    __tablename__ = "orders"
    id: Mapped[uuid.UUID] = mapped_column(primary_key=True, default=uuid.uuid4)

Note default=uuid.uuid4 passes the function, not a call — a classic bug is writing uuid.uuid4() and giving every row the same ID. For high-insert tables, consider a v7 default instead (PostgreSQL guide coming in this series).

Frequently asked questions

Does Python's uuid module support UUID v7?

From Python 3.14 the standard library includes uuid.uuid7(), so no dependency is needed. On 3.13 and earlier the module stops at v5, and the practical route is the uuid6 package from PyPI, which implements the RFC 9562 versions 6, 7 and 8: pip install uuid6, then from uuid6 import uuid7. The values it returns are ordinary uuid.UUID objects, so they slot into Django UUIDField, SQLAlchemy and psycopg without any adapter. Version matters here because v7 is what you want for database primary keys — its leading 48-bit millisecond timestamp keeps inserts appending to the right-hand edge of the index instead of scattering across it. If you are on an older interpreter and cannot add a dependency, generating the value in PostgreSQL 18 with uuidv7() is the other supported path.

How do I get a UUID without hyphens in Python?

Use the .hex attribute: uuid.uuid4().hex returns the 32-character lowercase hexadecimal string with the four hyphens removed. It is a plain str, so you can call .upper() on it if a system expects uppercase. Going the other way, uuid.UUID() parses the unhyphenated form without complaint — uuid.UUID('550e8400e29b41d4a716446655440000') is the same object as the hyphenated version, and the constructor also tolerates braces and a urn:uuid: prefix. Keep in mind that the hyphenless form is a display choice, not a different value: the underlying 128 bits are identical either way. If you are storing UUIDs rather than showing them, skip both text forms and store .bytes, which is 16 bytes instead of 32 or 36 characters.

Is uuid4() cryptographically secure?

Yes. CPython's uuid4() reads its randomness from os.urandom(), which draws from the operating system's cryptographically secure entropy source — getrandom() on Linux, BCryptGenRandom on Windows. That makes the output unpredictable even to someone who has observed many previously generated values, so uuid4() is safe for identifiers that must not be guessable, such as invite codes or unsubscribe links. Two caveats. First, uniqueness and unpredictability are different properties: a UUID is not a substitute for a signed token, because anyone holding it can use it. Second, if you fork a process or restore a VM snapshot, os.urandom() is reseeded correctly by the OS, but any userspace PRNG you cached is not — never swap in random.getrandbits() to make generation faster.

How do I validate a UUID string in Python?

Pass the string to the uuid.UUID() constructor inside a try/except ValueError block; it raises ValueError for anything that is not a well-formed UUID. That approach is preferable to a regular expression because it also normalises input, accepting hyphenless, braced and urn:uuid: forms, and it hands you a parsed object you can inspect afterwards. If you need to enforce a specific version, check the parsed result: uuid.UUID(value).version == 4. Be aware that the constructor is deliberately permissive about formatting, so if your API contract requires the exact canonical 36-character lowercase form, compare str(parsed) back against the original input, or pair the parse with a strict regex. For Pydantic or Django, use their built-in UUID field types, which run this validation for you.

Need a UUID right now without opening a REPL? Use our free UUID generator — v1/v3/v4/v5/v7, bulk up to 1,000, entirely in your browser.

References