Generate UUIDs in Python

Learn how to use Python's uuid module to generate universally unique identifiers for database keys, API resources, and distributed systems.

6 min read

The uuid module in the Python standard library generates universally unique identifiers (UUIDs), which are 128-bit values typically displayed as 36-character strings like 550e8400-e29b-41d4-a716-446655440000. Use UUIDs as primary keys in databases, identifiers for API resources, or tracing IDs in distributed systems where you cannot rely on a single auto-increment counter.

pythonpython
import uuid
 
new_id = uuid.uuid4()
print(new_id)         # 550e8400-e29b-41d4-a716-446655440000
print(str(new_id))  # 550e8400-e29b-41d4-a716-446655440000
print(new_id.hex)     # 550e8400e29b41d4a716446655440000

uuid.uuid4() generates a random UUID. The string representation includes dashes at positions 8, 13, 18, and 23. .hex returns the same value as a 32-character string without dashes.

UUID versions

Not every UUID is generated the same way, and picking the right version affects both privacy and database performance. The uuid module supports several versions, each with a different generation strategy.

pythonpython
import uuid
 
print("v1:", uuid.uuid1())  # v1: f47ac10b-58cc-11e8-ae4d-8c8590a91e3b
print("v4:", uuid.uuid4())  # v4: 550e8400-e29b-41d4-a716-446655440000
VersionMethodDescription
UUIDv1uuid.uuid1()Based on timestamp and MAC address. Unique but reveals when and where the ID was created.
UUIDv4uuid.uuid4()Fully random. Does not reveal timing or machine information. The safest choice for most applications.
UUIDv3uuid.uuid3(ns, name)MD5 hash of a namespace UUID and a name. Deterministic: same input always produces the same UUID.
UUIDv5uuid.uuid5(ns, name)SHA-1 hash of a namespace UUID and a name. Same as v3 but with a stronger hash.
UUIDv7uuid.uuid7()Time-ordered with random bits. Added in Python 3.14. Good for database primary keys.

For most applications, use UUIDv4. It is the simplest, safest, and most widely compatible option.

UUIDv7: time-ordered identifiers

UUIDv7, available since Python 3.14, combines a Unix timestamp with random bits. The timestamp prefix means UUIDs generated close together sort in chronological order, which improves database index performance compared to fully random UUIDv4.

pythonpython
import uuid
import time
 
for _ in range(3):
    print(uuid.uuid7())
    time.sleep(0.001)

Generating three UUIDs a millisecond apart shows the pattern clearly, since the leading segment of each value tracks the advancing timestamp while the rest stays effectively random:

texttext
018f5d80-7a3b-7000-8000-000000000001
018f5d80-7a3b-7000-8000-000000000002
018f5d80-7a3b-7000-8000-000000000003

The first part of each UUID increments because the timestamp advances. Unlike UUIDv1, UUIDv7 does not expose the MAC address. It is designed specifically for database primary keys where insertion-order locality matters.

If you are using an older Python version, UUIDv7 is not available from the standard library. Use UUIDv4 or a third-party library.

Deterministic UUIDs with v3 and v5

UUIDv3 and UUIDv5 produce the same UUID every time for the same namespace and name. This is useful when you need to derive a stable identifier from existing data.

pythonpython
import uuid
 
NAMESPACE_URL = uuid.UUID("6ba7b811-9dad-11d1-80b4-00c04fd430c8")
 
article_id = uuid.uuid5(NAMESPACE_URL, "https://example.com/articles/42")
print(article_id)  # de470051-1183-5a53-a13d-d33bc219f294

The same namespace and name always produce the same UUID. This is useful for content-addressable identifiers or generating IDs from stable input like URLs or filenames.

Predefined namespace UUIDs are available:

ConstantValue
uuid.NAMESPACE_DNS6ba7b810-9dad-11d1-80b4-00c04fd430c8
uuid.NAMESPACE_URL6ba7b811-9dad-11d1-80b4-00c04fd430c8
uuid.NAMESPACE_OID6ba7b812-9dad-11d1-80b4-00c04fd430c8
uuid.NAMESPACE_X5006ba7b814-9dad-11d1-80b4-00c04fd430c8

Use UUIDv5 over UUIDv3 when possible. SHA-1 is a stronger hash than MD5 and produces equally deterministic results.

Parsing UUID strings

Convert strings to UUID objects with the UUID constructor.

pythonpython
import uuid
 
raw = "550e8400-e29b-41d4-a716-446655440000"
uid = uuid.UUID(raw)
 
print(uid.version)  # 4
print(uid.variant)  # specified in RFC 4122
print(uid.bytes)      # b'U\x0e\x84\x00\xe2\x9bA\xd4\xa7\x16DfUD\x00\x00'

uid.version tells you which UUID version was used. uid.variant identifies the UUID layout. uid.bytes returns the raw 16 bytes.

The constructor accepts several formats:

pythonpython
uuid.UUID("550e8400-e29b-41d4-a716-446655440000")  # standard
uuid.UUID("550e8400e29b41d4a716446655440000")       # hex
uuid.UUID("{550e8400-e29b-41d4-a716-446655440000}") # with braces
uuid.UUID("urn:uuid:550e8400-e29b-41d4-a716-446655440000")  # URN

UUIDs as database primary keys

UUIDs are commonly used as primary keys instead of auto-increment integers. They have tradeoffs.

Advantages of UUID primary keys:

  • You can generate the ID in application code before inserting, so you know the ID immediately.
  • No collisions when merging data from multiple sources or databases.
  • They do not reveal information about how many records exist (unlike sequential integers).

Disadvantages:

  • UUIDs are 16 bytes versus 4 or 8 bytes for integers, so indexes are larger.
  • Random UUIDs (v4) cause index fragmentation in B-tree databases, slowing inserts.
  • UUIDs are not human-friendly for debugging or customer support references.

If the index fragmentation is a concern, use UUIDv7 (Python 3.14+) for time-ordered IDs that maintain database index performance.

Practical example: generating IDs for uploaded files

Generate a unique filename for each uploaded file to prevent name collisions.

pythonpython
import uuid
from pathlib import Path
 
def unique_filename(original_name):
    ext = Path(original_name).suffix
    return f"{uuid.uuid4().hex}{ext}"
 
names = ["report.pdf", "report.pdf", "photo.jpg"]
for name in names:
    print(unique_filename(name))

Both uploads named report.pdf get a different, unpredictable filename even though they share the original name, because each call to uuid4() generates a fresh random value:

texttext
a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6.pdf
f7e8d9c0b1a293847576655443322101.pdf
1234567890abcdef1234567890abcdef.jpg

Each file gets a unique hex name, avoiding collisions even when users upload files with identical names. The original extension is preserved for correct MIME type detection.

Common mistakes

Using UUIDv1 when privacy matters. UUIDv1 includes the machine's MAC address and the creation timestamp. Anyone who sees the UUID can determine when and (potentially) where it was generated. Use UUIDv4 for privacy-sensitive applications.

Relying on UUIDs for security through obscurity. UUIDs are identifiers, not secrets. If a UUID appears in a URL or API response, assume it is public. Do not use UUIDs as authentication tokens. For secure tokens, use the secrets module.

Calling uuid4 without storing the result. Every call to uuid.uuid4() returns a new random value. Store the result in a variable; do not call uuid.uuid4() multiple times expecting the same value.

Forgetting that UUIDv3/v5 are deterministic. Same inputs produce the same UUID. This is by design but can be surprising if you expect randomness.

Rune AI

Rune AI

Key Insights

  • Use uuid.uuid4() for random, universally unique identifiers.
  • Use uuid.uuid7() (Python 3.14+) for time-ordered UUIDs that sort well in databases.
  • Convert UUIDs to strings with str(uuid) or to hex with uuid.hex.
  • Parse UUID strings with uuid.UUID('...').
  • UUIDs are 128-bit values; the collision probability for v4 is effectively zero.
  • Avoid UUIDv1 when you do not want to expose the MAC address and timestamp.
RunePowered by Rune AI

Frequently Asked Questions

Which UUID version should I use?

Use UUIDv4 (random) for most purposes. It is simple, does not reveal timing or machine information, and the collision probability is astronomically low. Use UUIDv7 (time-ordered, added in Python 3.14) when you need sortable IDs for database indexes.

Are UUIDs guaranteed to be unique?

No system can absolutely guarantee uniqueness without a central authority. However, UUIDv4 generates 122 random bits, making the probability of a collision so low that it is effectively zero for practical purposes. You would need to generate billions of UUIDs per second for decades to have a meaningful chance of collision.

Can I convert a UUID to and from a string?

Yes. `str(my_uuid)` gives the standard 36-character string like `'550e8400-e29b-41d4-a716-446655440000'`. `uuid.UUID('550e8400-e29b-41d4-a716-446655440000')` parses the string back into a UUID object. You can also use `.hex` for a 32-character string without dashes.

Conclusion

The uuid module is the standard way to generate unique identifiers in Python. Use uuid.uuid4() for random IDs that do not reveal any information, and uuid.uuid7() when you need time-ordered IDs for database performance. UUIDs are ideal for distributed systems where you cannot rely on a central auto-increment counter.