mrkeyoor.com_
Sat 08 Aug 21:01 UTC
PyPIUtilsupdated 08 Aug 2026

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.

Verdict

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.

API stability5/5Version 1.0.13 preserves module-level helpers for backward compatibility while exposing the safer ShortUUID instance API. The core `uuid`, `random`, `encode`, `decode`, and alphabet methods are small and clearly implemented. The one historical break is documented: versions before 1.0.0 reversed significance and now require the explicit legacy decoding path.
Docs4/5The README covers random and named UUIDs, secure random strings, the default regex, alphabet sorting, interoperability mode, truncation, reversible encoding, per-instance alphabets, CLI use, Django fields, and pre-1.0 migration. It is unusually complete for a small utility, though some installation text still mentions easy_install and an old source repository URL.
Maintenance4/5The latest PyPI release was uploaded on March 11, 2024, but the repository was pushed on June 20, 2026 and currently shows no open issues or pull requests. The slow release pace is reasonable for a narrow, dependency-free codec, and recent source work shows it is not abandoned. Consumers should still pin behavior because custom encodings become stored data.
Ecosystem4/5The project has 2,194 GitHub stars, works entirely with Python's standard UUID and secrets facilities, provides a console command, includes typing metadata, and offers a Django field without making Django mandatory. Cross-language use is possible when alphabet ordering is fixed, but ShortUUID is less standardized than UUID, ULID, or UUID textual forms.

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
Skip it if

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) == 22

This 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) == original

Pass 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 None

This validates only the default 22-character text shape. It does not prove the value was generated by your application or decode it safely.

Alternatives

PackageRegistryPick it when
nanoidPyPIYou want compact secure random strings and do not need reversible UUID encoding
ulid-pyPyPIYou want lexicographically sortable identifiers with a timestamp component
sqidsPyPIYou need short reversible encodings of integer tuples rather than random 128-bit identities