shortuuid
shortuuid encodes ordinary 128-bit UUIDs with a 57-character alphabet chosen to avoid look-alike characters, producing a typical 22-character URL-safe identifier instead of the usual 36-character UUID text. It can generate UUID4 or deterministic UUID5 values, reversibly encode existing UUID objects, make secure random strings, use custom alphabets, expose a CLI, and provide an optional Django CharField.
Use shortuuid when the requirement is specifically a compact textual UUID with reversible encoding. For sortable IDs, arbitrary short tokens, or encoded database integers, choose the format that directly models that need.
Use it if
- You need reversible, human-friendlier text for existing UUID values without changing their 128-bit identity
- You want URL-safe random IDs that avoid characters such as zero, capital O, one, lowercase l, and capital I
- You need deterministic short IDs derived from DNS names or URLs through UUID5
- A per-instance custom alphabet must interoperate with another ShortUUID implementation
- You need sortable identifiers; encoded UUID4 values remain random, so ULID or a time-ordered UUID format is a better fit for ordered indexes
- You plan to truncate IDs without collision analysis; the README says truncated values are no longer universally unique
- You need opaque secrets with expiration, authorization, or revocation; an identifier generator does not provide token lifecycle or access control
- You cannot preserve the exact alphabet across services; decoding is alphabet-dependent, and the source sorts and deduplicates custom alphabets by default
- You expect the Django field to store reversible UUID4 values; its source generates a secure random string of the requested length and subclasses CharField rather than UUIDField
Setup reality
`pip install shortuuid` installs a pure-Python package with no declared runtime dependencies; version 1.0.13 declares Python 3.6 or newer and includes typing metadata. The default call returns a 22-character encoding of a UUID4, while `random()` returns characters chosen with Python's `secrets` module and is not tied to a UUID. Decide which contract you need before storing values. Encoding and decoding are reversible only when every producer and consumer uses the same alphabet and ordering. A custom alphabet is deduplicated and sorted unless `dont_sort_alphabet=True`; setting the module-level alphabet mutates a shared global instance, so libraries, threads, and tests can surprise one another. Prefer separate `ShortUUID` objects for different alphabets. Shortening by slicing discards UUID bits and changes the collision model, so enforce a database uniqueness constraint and retry generation if you choose a shorter length. Values stored by releases before 1.0.0 used reversed byte significance and require `decode(..., legacy=True)` during migration; the README says that compatibility option may disappear. The Django `ShortUUIDField` is optional and only imports when Django is available. It calculates `max_length` from prefix plus length unless you override it and generates random strings, so schema migrations and uniqueness settings are still your responsibility. The CLI prints one value but provides no coordination across machines.
Patterns
Generate a default short UUIDgenerate-short-uuid
import shortuuid
public_id = shortuuid.uuid()
assert len(public_id) == 22This is a base57 encoding of a UUID4. It is compact text, but it is not time-sortable.
Encode and recover an existing UUIDencode-existing-uuid
import uuid
import shortuuid
original = uuid.UUID('6ca4f0f8-2508-4bac-b8f1-5d1e3da2247a')
encoded = shortuuid.encode(original)
assert shortuuid.decode(encoded) == originalPass a `uuid.UUID` object to encode, not its string form. Decoding requires the same alphabet used for encoding.
Derive a stable ID from a namecreate-deterministic-id
import shortuuid
customer_id = shortuuid.uuid(name='customer.example.com')
assert customer_id == shortuuid.uuid(name='customer.example.com')Names beginning with http or https use the URL namespace; other names use the DNS namespace. Changing the input spelling changes the result.
Generate a secure random stringgenerate-random-token
from shortuuid import ShortUUID
generator = ShortUUID()
invite_code = generator.random(length=16)`random()` uses `secrets.choice` and is not reversible to a UUID. A shorter length has a smaller collision space, so keep a uniqueness constraint.
Create IDs with a per-instance alphabetuse-custom-alphabet
from shortuuid import ShortUUID
hexish = ShortUUID(alphabet='0123456789abcdef')
value = hexish.uuid()
assert set(value) <= set('0123456789abcdef')The alphabet is sorted and duplicates are removed by default. Keep the instance rather than changing the module-global alphabet.
Preserve an external alphabet's orderingpreserve-alphabet-order
from shortuuid import ShortUUID
alphabet = 'abcdefgh1230'
codec = ShortUUID(alphabet=alphabet, dont_sort_alphabet=True)
encoded = codec.uuid()Set `dont_sort_alphabet=True` only when interoperability requires this exact character order. Order is part of the encoded data format.
Calculate length for a byte countcalculate-encoded-length
from shortuuid import ShortUUID
codec = ShortUUID()
full_uuid_chars = codec.encoded_length(16)
eight_byte_chars = codec.encoded_length(8)This reports how many characters can represent the requested number of bytes with the active alphabet; it does not generate a value.
Migrate a pre-1.0 encoded valuedecode-legacy-value
import shortuuid
old_value = row['old_short_uuid']
uuid_value = shortuuid.decode(old_value, legacy=True)
row['short_uuid'] = shortuuid.encode(uuid_value)Use legacy decoding only for values created before 1.0.0. Rewrite stored values because the README says this option may be removed.
Keep two alphabets isolatedavoid-global-alphabet
from shortuuid import ShortUUID
public_ids = ShortUUID()
numeric_ids = ShortUUID(alphabet='0123456789')
public_value = public_ids.uuid()
numeric_value = numeric_ids.uuid()The module-level `set_alphabet()` changes a shared global codec. Separate instances avoid cross-test and cross-thread surprises.
Use the optional Django model fielddefine-django-field
from django.db import models
from shortuuid.django_fields import ShortUUIDField
class Order(models.Model):
id = ShortUUIDField(
length=22,
prefix='ord_',
unique=True,
primary_key=True,
)This is a CharField whose default is a secure random string, not a UUIDField encoding. Create and apply a Django migration after adding it.
Retry a shortened code on uniqueness collisionretry-on-collision
from shortuuid import ShortUUID
codec = ShortUUID()
for _ in range(5):
code = codec.random(length=10)
if not repository.exists(code):
repository.insert(code)
break
else:
raise RuntimeError('could not allocate unique code')An application-side existence check can race. Enforce uniqueness in the database and retry the insert when the constraint rejects a collision.
Validate the default alphabet and lengthvalidate-default-format
import re
SHORT_UUID_RE = re.compile(r'^[2-9A-HJ-NP-Za-km-z]{22}$')
def is_short_uuid_text(value: str) -> bool:
return SHORT_UUID_RE.fullmatch(value) is not NoneThis validates only the default 22-character text shape. It does not prove the value was generated by your application or decode it safely.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| nanoid | PyPI | You want compact secure random strings and do not need reversible UUID encoding |
| ulid-py | PyPI | You want lexicographically sortable identifiers with a timestamp component |
| sqids | PyPI | You need short reversible encodings of integer tuples rather than random 128-bit identities |