Python uuid
last modified October 29, 2026 Python 3.15
In this article we show how to generate and work with universally unique
identifiers using the uuid module.
A UUID (universally unique identifier) is a 128-bit number used to identify objects without a central authority. Two systems can create UUIDs independently and, for practical purposes, never produce the same value. UUIDs are used as database keys, request and trace identifiers, file names, and message identifiers.
A UUID is usually written as 32 hexadecimal digits in five groups separated
by hyphens, for example 6ba7b810-9dad-11d1-80b4-00c04fd430c8.
The format and the generation methods are defined in RFC 9562, which
replaced RFC 4122 in 2024. Four bits of every UUID hold its
version, the method used to create it, and two or three bits hold
the variant, the layout of the remaining bits.
| Version | Function | Based on |
|---|---|---|
| 1 | uuid1 | time and MAC address |
| 3 | uuid3 | MD5 hash of a namespace and a name |
| 4 | uuid4 | random numbers |
| 5 | uuid5 | SHA-1 hash of a namespace and a name |
| 6 | uuid6 | time and node, reordered for sorting (3.14) |
| 7 | uuid7 | Unix time in milliseconds and random bits (3.14) |
| 8 | uuid8 | custom data (3.14) |
Versions 6, 7, and 8 and the NIL and MAX constants
were added in Python 3.14.
A random UUID
The uuid4 function returns a random UUID. It is the most common
choice when we need a unique value and nothing else.
import uuid u = uuid.uuid4() print(u) print(repr(u)) print(u.version) print(u.variant) print(u.hex) print(u.int) print(u.bytes) print(u.urn)
The function returns a UUID object. Converting it to a string
gives the standard hyphenated form.
print(u.hex) print(u.int) print(u.bytes) print(u.urn)
The object offers several representations: 32 hexadecimal digits, a 128-bit
integer, 16 bytes in big-endian order, and a URN. The version
and variant attributes tell how the UUID was created. The
variant string still refers to RFC 4122, because RFC 9562 uses the same
variant.
$ python first_uuid.py
00580c74-5b62-4ffc-a6d3-5c8942e69d47
UUID('00580c74-5b62-4ffc-a6d3-5c8942e69d47')
4
specified in RFC 4122
00580c745b624ffca6d35c8942e69d47
457174731215148657636273659924880711
b'\x00X\x0ct[bO\xfc\xa6\xd3\\\x89B\xe6\x9dG'
urn:uuid:00580c74-5b62-4ffc-a6d3-5c8942e69d47
How unique is random
A version 4 UUID has 122 random bits; the other six bits encode the version and variant.
import uuid
ids = [uuid.uuid4() for _ in range(5)]
for u in ids:
print(u, u.version)
print(len({uuid.uuid4() for _ in range(100_000)}))
print(f'{2 ** 122:,}')
We generate 100,000 UUIDs and put them in a set; none of them repeat. There
are 2122 possible values. Even after generating a billion UUIDs per
second for 85 years, the chance of a single collision would be about 50%.
The uuid4 function takes its bits from os.urandom,
so the values are also unpredictable.
$ python random_uuids.py 4f172344-f989-4981-bdd1-38eb2e458197 4 02eb18c1-3738-4459-9faa-ec584651780c 4 5639f75c-a403-4938-b4b6-de11e8eed6d4 4 679bc964-354c-4412-956b-d64c99f72be9 4 df770309-5709-4014-af3b-70c5bc8f3f95 4 100000 5,316,911,983,139,663,491,615,228,241,121,378,304
Parsing UUIDs
The UUID constructor accepts a string in several formats, or the
bytes, int, fields, and other keyword
arguments.
import uuid
forms = [
'6ba7b810-9dad-11d1-80b4-00c04fd430c8',
'{6BA7B810-9DAD-11D1-80B4-00C04FD430C8}',
'urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8',
'6ba7b8109dad11d180b400c04fd430c8',
]
values = {uuid.UUID(s) for s in forms}
print(values)
u = uuid.UUID(bytes=bytes(range(16)))
print(u)
print(uuid.UUID(int=1))
try:
uuid.UUID('6ba7b810-9dad-11d1-80b4')
except ValueError as e:
print('ValueError:', e)
The constructor ignores braces, the urn:uuid: prefix, hyphens,
and the case of the digits. All four strings give the same UUID, so the set
contains a single element.
u = uuid.UUID(bytes=bytes(range(16))) print(uuid.UUID(int=1))
We create UUIDs from 16 bytes and from an integer. A string with the wrong
number of digits raises a ValueError, which makes the
constructor useful for validating input.
$ python parse_uuid.py
{UUID('6ba7b810-9dad-11d1-80b4-00c04fd430c8')}
00010203-0405-0607-0809-0a0b0c0d0e0f
00000000-0000-0000-0000-000000000001
ValueError: badly formed hexadecimal UUID string
Fields
The fields attribute splits a UUID into the six fields of the
original version 1 layout.
import uuid
u = uuid.UUID('6ba7b810-9dad-11d1-80b4-00c04fd430c8')
print(u.fields)
print(f'time_low {u.time_low:08x}')
print(f'time_mid {u.time_mid:04x}')
print(f'time_hi_version {u.time_hi_version:04x}')
print(f'clock_seq_hi_variant {u.clock_seq_hi_variant:02x}')
print(f'clock_seq_low {u.clock_seq_low:02x}')
print(f'node {u.node:012x}')
print(u.version)
The UUID used here is NAMESPACE_DNS, a version 1 UUID. In
version 1, the first three fields hold parts of a timestamp, the fourth and
fifth hold the clock sequence and the variant, and the last one the node,
usually a MAC address. For other versions, the names of the fields no longer
describe their content, but the attributes still work.
$ python fields.py (1806153744, 40365, 4561, 128, 180, 825973027016) time_low 6ba7b810 time_mid 9dad time_hi_version 11d1 clock_seq_hi_variant 80 clock_seq_low b4 node 00c04fd430c8 1
Time-based UUIDs
The uuid1 function combines the current time, a clock sequence,
and a node identifier.
import secrets
import uuid
from datetime import datetime, timedelta, UTC
node = secrets.randbits(48) | (1 << 40)
u = uuid.uuid1(node=node)
print(u, u.version)
epoch = datetime(1582, 10, 15, tzinfo=UTC)
created = epoch + timedelta(microseconds=u.time // 10)
print(created)
print(f'{u.node:012x}')
print(u.clock_seq)
By default, the node is the MAC address of a network interface, returned by
uuid.getnode. Every UUID then reveals which machine created it
and when, which is a privacy problem. In this example, we pass a random node
instead.
node = secrets.randbits(48) | (1 << 40)
RFC 9562 recommends setting the multicast bit of a random node, so that it cannot clash with a real MAC address.
epoch = datetime(1582, 10, 15, tzinfo=UTC) created = epoch + timedelta(microseconds=u.time // 10)
The time attribute is the number of 100-nanosecond intervals
since 15 October 1582, the start of the Gregorian calendar. We convert it to
a datetime. The clock sequence is a random 14-bit number that protects
against duplicates if the clock goes backwards.
$ python time_based.py 762d9e38-d3e1-11f1-a975-23537ff974c0 1 2026-10-29 21:41:12.899948+00:00 23537ff974c0 10613
Name-based UUIDs
The uuid3 and uuid5 functions create a UUID from a
namespace UUID and a name. The same input always gives the same UUID.
import uuid
print(uuid.uuid5(uuid.NAMESPACE_DNS, 'zetcode.com'))
print(uuid.uuid5(uuid.NAMESPACE_DNS, 'zetcode.com'))
print(uuid.uuid5(uuid.NAMESPACE_URL, 'https://zetcode.com/python/'))
print(uuid.uuid3(uuid.NAMESPACE_DNS, 'zetcode.com'))
print(uuid.NAMESPACE_DNS)
products = uuid.uuid5(uuid.NAMESPACE_DNS, 'products.example.com')
for sku in ['KB-101', 'MS-220', 'KB-101']:
print(sku, uuid.uuid5(products, sku))
Version 5 uses SHA-1 and version 3 uses MD5; prefer version 5. The module defines namespaces for domain names, URLs, ISO OIDs, and X.500 names.
products = uuid.uuid5(uuid.NAMESPACE_DNS, 'products.example.com')
for sku in ['KB-101', 'MS-220', 'KB-101']:
print(sku, uuid.uuid5(products, sku))
Any UUID can be a namespace. We create a namespace for our product catalog and derive stable identifiers from product codes. Different systems that import the same product compute the same UUID without talking to each other.
$ python namespaced.py 881419e6-9386-5855-b0a9-1216bdd46c43 881419e6-9386-5855-b0a9-1216bdd46c43 2b0c3b38-d947-51ac-8f72-39229057f75c c695cbd5-410f-358c-b204-ff4ee15e81d8 6ba7b810-9dad-11d1-80b4-00c04fd430c8 KB-101 409f8e28-3954-5903-b237-da8f90d90d41 MS-220 7d9942a6-a950-55d9-b99a-6af748903cef KB-101 409f8e28-3954-5903-b237-da8f90d90d41
UUID version 7
A version 7 UUID starts with a 48-bit Unix timestamp in milliseconds, followed by a counter and random bits. Values created later sort after earlier ones.
import uuid
from datetime import datetime, UTC
u = uuid.uuid7()
print(u, u.version)
ms = u.int >> 80
print(ms)
print(datetime.fromtimestamp(ms / 1000, tz=UTC))
for _ in range(4):
print(uuid.uuid7())
The top 48 bits of the 128-bit integer hold the timestamp, so we shift the integer right by 80 bits to get it.
ms = u.int >> 80 print(datetime.fromtimestamp(ms / 1000, tz=UTC))
The timestamp is visible to anyone who sees the UUID. Do not use version 7 when the creation time of a record must stay private.
for _ in range(4):
print(uuid.uuid7())
UUIDs created within the same millisecond differ only in the counter and the random tail. Python's implementation increments the counter for each UUID in the same millisecond, so they still increase.
$ python uuid7_demo.py 01a1899c-205e-7195-b187-95c552f581d8 7 1793310072926 2026-10-29 21:41:12.926000+00:00 01a1899c-205e-7195-b187-95c655818385 01a1899c-205e-7195-b187-95c7be7ec982 01a1899c-205e-7195-b187-95c896b45cec 01a1899c-205e-7195-b187-95c9c3bd5e9d
Sort order
Random UUIDs scatter new rows across a database index. Time-ordered UUIDs are inserted at the end, like auto-incrementing integers, which keeps B-tree indexes compact.
import uuid
v4 = [uuid.uuid4() for _ in range(1000)]
v7 = [uuid.uuid7() for _ in range(1000)]
print(v4 == sorted(v4))
print(v7 == sorted(v7))
print([str(u) for u in v7] == sorted(str(u) for u in v7))
print(len({u.int >> 80 for u in v7}), 'distinct milliseconds')
We generate 1000 UUIDs of versions 4 and 7 and check whether the creation
order is the sorted order. For version 7, it is, both for the
UUID objects and their strings. The 1000 UUIDs were created
within a few milliseconds; the counter keeps them ordered.
$ python sort_order.py False True True 2 distinct milliseconds
UUID version 6
Version 6 contains the same data as version 1, but the timestamp is stored with its most significant bits first.
import uuid
node = 0x0a0b0c0d0e0f
v1 = uuid.uuid1(node=node, clock_seq=0)
v6 = uuid.uuid6(node=node, clock_seq=0)
print(f'timestamp {v1.time:015x}')
print(f'uuid1 {v1}')
print(f'uuid6 {v6}')
print(v6.time - v1.time)
Version 1 starts with the lowest 32 bits of the timestamp, which change the fastest, so its values do not sort by time. Version 6 stores the timestamp in order and sorts correctly. RFC 9562 recommends version 7 for new systems; version 6 is meant for systems that already use version 1.
$ python uuid6_demo.py timestamp 1f1d3e176365e2e uuid1 76365e2e-d3e1-11f1-8000-0a0b0c0d0e0f uuid6 1f1d3e17-6365-6e7d-8000-0a0b0c0d0e0f 79
UUID version 8
Version 8 lets applications put their own data into a UUID. The
uuid8 function takes three blocks: 48 bits, 12 bits, and 62 bits.
import uuid u = uuid.uuid8(a=0x123456789abc, b=0xdef, c=0x123456789abcdef) print(u, u.version) print(uuid.uuid8().version) tenant = 42 shard = 7 seq = 1001 custom = uuid.uuid8(a=tenant, b=shard, c=seq) print(custom) print(custom.int >> 80, (custom.int >> 64) & 0xfff, custom.int & ((1 << 62) - 1))
The remaining six bits hold the version and variant. Blocks that are not
given are filled with random bits, so uuid8() without arguments
returns a random UUID.
custom = uuid.uuid8(a=tenant, b=shard, c=seq)
We encode a tenant, a shard, and a sequence number and extract them back with shifts and masks. Such UUIDs are unique only if the application makes sure the combinations are.
$ python uuid8_demo.py 12345678-9abc-8def-8123-456789abcdef 8 8 00000000-002a-8007-8000-0000000003e9 42 7 1001
NIL and MAX
The NIL UUID has all bits set to zero and the MAX
UUID has all bits set to one.
import uuid
print(uuid.NIL)
print(uuid.MAX)
print(uuid.NIL.int, uuid.MAX.int == 2 ** 128 - 1)
def find_user(user_id=uuid.NIL):
if user_id == uuid.NIL:
return 'anonymous'
return f'user {user_id}'
print(find_user())
print(find_user(uuid.UUID('9c2f1b4e-8d3a-4f6b-a1c2-3e4d5f6a7b8c')))
The special values can be used as placeholders, for example to mark a
missing identifier in a column that does not allow NULL, or as
the lowest and highest bounds of a range.
$ python nil_max.py 00000000-0000-0000-0000-000000000000 ffffffff-ffff-ffff-ffff-ffffffffffff 0 True anonymous user 9c2f1b4e-8d3a-4f6b-a1c2-3e4d5f6a7b8c
Comparing UUIDs
UUID objects are compared by their integer values. They are
immutable and hashable, so they can be used as dictionary keys and set
members.
import uuid
a = uuid.UUID('00000000-0000-0000-0000-000000000002')
b = uuid.UUID('00000000-0000-0000-0000-00000000000a')
print(a < b)
print(a == str(a))
print(str(a).upper() == str(a))
print(uuid.UUID(str(a).upper()) == a)
users = {a: 'jane', b: 'john'}
print(users[uuid.UUID('00000000000000000000000000000002')])
A UUID is never equal to a string, even to its own string form. Strings in
upper case or without hyphens differ from the canonical form, too. Convert
input to UUID objects before comparing or looking up values.
$ python compare_uuids.py True False True True jane
UUIDs in JSON
The json module does not know how to serialize
UUID objects.
import json
import uuid
order = {'id': uuid.uuid7(), 'items': 3}
try:
json.dumps(order)
except TypeError as e:
print('TypeError:', e)
data = json.dumps(order, default=str)
print(data)
loaded = json.loads(data)
loaded['id'] = uuid.UUID(loaded['id'])
print(loaded)
The default function is called for objects that
json cannot serialize; str turns a UUID into its
canonical form. When loading, we convert the string back to a
UUID.
$ python json_uuid.py
TypeError: Object of type UUID is not JSON serializable
{"id": "01a1899c-20b4-7367-9ad4-2af79f5c7323", "items": 3}
{'id': UUID('01a1899c-20b4-7367-9ad4-2af79f5c7323'), 'items': 3}
Short identifiers
The canonical form has 36 characters. Encoding the 16 bytes with URL-safe Base64 gives a 22-character string, which is handy in URLs.
import base64
import uuid
def short(u):
return base64.urlsafe_b64encode(u.bytes).rstrip(b'=').decode()
def expand(s):
return uuid.UUID(bytes=base64.urlsafe_b64decode(s + '=='))
u = uuid.uuid4()
s = short(u)
print(u, len(str(u)))
print(s, len(s))
print(expand(s) == u)
We remove the two padding characters when encoding and add them back when decoding. The base64 article covers the encoding in more detail.
$ python short_ids.py c99341e5-6888-48e8-8d2a-7d061f9ce994 36 yZNB5WiISOiNKn0GH5zplA 22 True
Storing UUIDs in SQLite
SQLite has no UUID type. We can store UUIDs as 16-byte BLOBs, which take less than half the space of strings.
import sqlite3
import uuid
sqlite3.register_adapter(uuid.UUID, lambda u: u.bytes)
sqlite3.register_converter('UUID', lambda b: uuid.UUID(bytes=b))
con = sqlite3.connect(':memory:', detect_types=sqlite3.PARSE_DECLTYPES)
con.execute('CREATE TABLE events (id UUID PRIMARY KEY, name TEXT)')
for name in ['created', 'paid', 'shipped', 'delivered']:
con.execute('INSERT INTO events VALUES (?, ?)', (uuid.uuid7(), name))
for event_id, name in con.execute('SELECT id, name FROM events ORDER BY id'):
print(type(event_id).__name__, event_id, name)
print(con.execute('SELECT length(id) FROM events LIMIT 1').fetchone())
An adapter converts UUID objects to bytes when they are passed
as query parameters. A converter turns columns declared as
UUID back into objects; it is enabled with
detect_types=sqlite3.PARSE_DECLTYPES.
for event_id, name in con.execute('SELECT id, name FROM events ORDER BY id'):
Bytes are compared in big-endian order, the same order as the integers, so ordering by the version 7 key returns the events in the order they were created.
$ python sqlite_uuid.py UUID 01a1899c-20d2-7165-915d-93750a518033 created UUID 01a1899c-20d2-7165-915d-9376bc1005f3 paid UUID 01a1899c-20d2-7165-915d-937768f7daf8 shipped UUID 01a1899c-20d2-7165-915d-9378e1b6966e delivered (16,)
Command line interface
The module can be run as a script to generate UUIDs from the command line.
Without options, the command prints a version 4 UUID. The -u
option selects the function and the -C option, added in Python
3.14, generates several UUIDs at once. For versions 3 and 5, -n
gives the namespace, either a UUID or one of the predefined names
@dns, @url, @oid, and
@x500, and -N the name.
$ python -m uuid 56c3bf19-e473-4a7e-bced-d4dddfc0785c $ python -m uuid -u uuid7 -C 3 01a1899c-20fd-749e-bb61-b67efddedee2 01a1899c-20fd-749e-bb61-b67fc380e8ee 01a1899c-20fd-749e-bb61-b680720622bf $ python -m uuid -u uuid5 -n @dns -N zetcode.com 881419e6-9386-5855-b0a9-1216bdd46c43 $ python -m uuid -u uuid3 -n @url -N https://zetcode.com/ db5b3474-1a73-3515-9f97-61be00de4531
Source
uuid – UUID objects according to RFC 9562
RFC 9562 – Universally Unique IDentifiers (UUIDs)
In this article we have generated and used UUIDs with the Python
uuid module.
Author
List all Python tutorials.