Python 3.13, released in October 2024, added uuid.uuid7() and uuid.uuid8() to the standard library’s uuid module. For projects on Python 3.13+, UUID v7 generation now requires no third-party dependency — it is a single import and a function call.

The new functions

import uuid

# UUID v7 — time-ordered, RFC 9562-compliant
u7 = uuid.uuid7()
print(u7)
# 019236a7-b4f2-7000-8d3e-9c1a2b3d4e5f

# UUID v8 — custom/application-defined layout
# Takes a 128-bit integer; you control all non-fixed bits
u8 = uuid.uuid8(hi=0x0123456789ab, mid=0xcdef, lo=0x0123456789abcdef)
print(u8)

Both return a uuid.UUID object — the same type returned by uuid.uuid4(). All existing code that works with UUID objects (comparisons, string conversion, JSON serialisation via str(u)) works unchanged.

uuid.uuid7() implementation details

The Python 3.13 implementation follows RFC 9562 §5.7 exactly:

The monotonic counter ensures that two calls to uuid.uuid7() within the same millisecond produce values that sort in the order they were generated. The counter resets when the millisecond advances and is not shared across processes.

Upgrading from uuid6

If your project uses the uuid6 third-party package for Python 3.9–3.12 compatibility, the migration to the standard library is straightforward:

# Before (uuid6 package)
import uuid6
user_id = uuid6.uuid7()

# After (Python 3.13+ standard library)
import uuid
user_id = uuid.uuid7()

The return type is identical — both return a uuid.UUID object with the same interface. String output format is the same. No other code changes are needed.

For packages that need to support Python 3.9–3.13, use a compatibility shim:

import sys

if sys.version_info >= (3, 13):
    from uuid import uuid7
else:
    from uuid6 import uuid7  # pip install uuid6

Django integration

Django’s UUIDField accepts any uuid.UUID value. To use v7 as a primary key default:

import uuid
from django.db import models

class Event(models.Model):
    id = models.UUIDField(
        primary_key=True,
        default=uuid.uuid7,  # Pass the callable, not the result
        editable=False,
    )
    name = models.CharField(max_length=255)
    created_at = models.DateTimeField(auto_now_add=True)

Django calls the default callable on each new instance. The UUIDField stores the 128-bit value as uuid in PostgreSQL and as CHAR(32) (hex, no hyphens) in SQLite and MySQL.

SQLAlchemy integration

import uuid
from sqlalchemy.orm import DeclarativeBase, mapped_column, Mapped
from sqlalchemy.dialects.postgresql import UUID

class Base(DeclarativeBase):
    pass

class Order(Base):
    __tablename__ = "orders"

    id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True),
        primary_key=True,
        default=uuid.uuid7,
    )

Using UUID(as_uuid=True) stores the value natively in PostgreSQL’s uuid column type and returns it as a Python uuid.UUID object rather than a string.

FastAPI and Pydantic

Pydantic v2 handles uuid.UUID natively. FastAPI path parameters and response models work without any change:

import uuid
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
    id: uuid.UUID
    name: str

@app.post("/items/", response_model=Item)
async def create_item(name: str):
    return Item(id=uuid.uuid7(), name=name)

Pydantic serialises uuid.UUID to a string in the canonical 8-4-4-4-12 format. Both v4 and v7 values pass validation as uuid.UUID — the type does not check the version digit.

What about uuid.uuid8()?

uuid.uuid8() takes a single 128-bit integer argument and sets the version and variant bits automatically. The remaining 122 bits are whatever you provide. It is intended for application-defined UUID formats that need to signal “this is a UUID-shaped thing” without following any standard layout.

In practice most applications should not use v8. It exists for specialised cases: embedding application-specific metadata in an identifier, implementing a custom time-ordered format, or encoding a foreign key relationship in the UUID itself. Unless you have a specific reason to control the bit layout, use v7 for time-ordered keys and v4 for random keys.

Version support summary

Python versionuuid.uuid7()Package required
3.13+Built-inNone
3.9–3.12Not built-inpip install uuid6
< 3.9Not available

Further reading

Frequently asked questions

How do I generate UUID v7 in Python?

In Python 3.13+, use uuid.uuid7() from the standard library. For Python 3.9–3.12, install the uuid6 package (pip install uuid6) and use uuid6.uuid7(). Both return a uuid.UUID object with the same interface as uuid.uuid4().

Does Python support UUID v7?

Yes, from Python 3.13 via uuid.uuid7() in the standard library. For Python 3.9–3.12, install the uuid6 package which provides the same interface.

What column type should I use to store a UUID in a database?

Use the native type where one exists — uuid in PostgreSQL, uniqueidentifier in SQL Server. In MySQL use BINARY(16) and store raw bytes. Avoid VARCHAR(36): it costs 36 bytes per row versus 16 for binary and makes every comparison a string operation.

What Python version do I need for uuid.uuid7()?

Python 3.13 or later. For Python 3.9–3.12, install the uuid6 package (pip install uuid6) which provides uuid6.uuid7() with the same interface.

Is uuid.uuid7() in Python 3.13 RFC 9562-compliant?

Yes. The implementation follows RFC 9562 §5.7 and includes a sub-millisecond counter to maintain sort order for UUIDs generated within the same millisecond, matching the Method 1 monotonic behaviour described in §6.2.