UPLID
██╗ ██╗██████╗ ██╗ ██╗██████╗
██║ ██║██╔══██╗██║ ██║██╔══██╗
██║ ██║██████╔╝██║ ██║██║ ██║
██║ ██║██╔═══╝ ██║ ██║██║ ██║
╚██████╔╝██║ ███████╗██║██████╔╝
╚═════╝ ╚═╝ ╚══════╝╚═╝╚═════╝
Stripe-style IDs for Python.
# Before: WTF is this?
"550e8400-e29b-41d4-a716-446655440000"
# After: It's a user.
"usr_0M3xL9kQ7vR2nP5wY1jZ4c"
Install
pip install uplid
Requires Python 3.14+ and Pydantic 2.10+.
Quick Start
>>> from uplid import UPLID
>>> UPLID.generate("usr")
usr_0M3xL9kQ7vR2nP5wY1jZ4c
>>> UPLID.generate("ord")
ord_7x9KmNpQrStUvWxYz012Ab
Why UPLID?
Debuggable - See usr_ in your logs and instantly know it's a user, not an order, not a session, not a mystery.
# Your logs now:
"User usr_0M3xL9kQ7vR2nP5wY1jZ4c created order ord_1a2B3c4D5e6F7g..."
# vs the nightmare:
"User 550e8400-e29b-41d4... created order 7c9e6679-7425-40de..."
Type-safe - Your type checker catches user_id = order_id mistakes before they hit production.
UserId = UPLID[Literal["usr"]]
OrgId = UPLID[Literal["org"]]
def get_user(user_id: UserId) -> User: ...
get_user(org_id) # Type error! Caught by mypy/pyright/ty
Time-sortable - Built on UUIDv7. Sort by ID = sort by creation time. No extra column needed.
URL-safe - 26 characters, no special characters, no encoding. usr_0M3xL9kQ7vR2nP5wY1jZ4c
Minimal dependencies - Just Pydantic. UUID generation uses Python 3.14's stdlib uuid7().
Inspired by Stripe's prefixed IDs (
sk_live_...,cus_...,pi_...) - the same pattern trusted by millions of API calls daily.
Pydantic Integration
from typing import Literal
from pydantic import BaseModel, Field
from uplid import UPLID, factory
UserId = UPLID[Literal["usr"]]
class User(BaseModel):
id: UserId = Field(default_factory=factory(UserId))
name: str
user = User(name="Alice")
user.model_dump() # {"id": "usr_0M3xL9kQ7vR2nP5wY1jZ4c", "name": "Alice"}
User(id="org_xxx...", name="Bad") # ValidationError: wrong prefix
FastAPI Integration
from typing import Annotated, Literal
from fastapi import Depends, FastAPI, HTTPException
from uplid import UPLID, UPLIDError, parse
UserId = UPLID[Literal["usr"]]
parse_user_id = parse(UserId)
def validate_user_id(user_id: str) -> UserId:
try:
return parse_user_id(user_id)
except UPLIDError as e:
raise HTTPException(422, str(e)) from None
@app.get("/users/{user_id}")
def get_user(user_id: Annotated[UserId, Depends(validate_user_id)]) -> User:
... # user_id is validated and typed
SQLAlchemy Integration
from uplid import UPLID, factory
from uplid.sqlalchemy import uplid_column
UserId = UPLID[Literal["usr"]]
class User(Base):
__tablename__ = "users"
id: Mapped[UserId] = uplid_column(UserId, primary_key=True)
name: Mapped[str]
# Stores as TEXT, returns as UPLID objects
user = session.execute(select(User)).scalar_one()
user.id.prefix # "usr"
user.id.datetime # When the ID was created
SQLModel Integration
from uplid import UPLID, factory
from uplid.sqlalchemy import uplid_field
UserId = UPLID[Literal["usr"]]
class User(SQLModel, table=True):
id: UserId = uplid_field(UserId, default_factory=factory(UserId), primary_key=True)
name: str
user.model_dump() # {"id": "usr_...", "name": "Alice"} - Pydantic just works
Prefix Rules
Prefixes must be letters and single underscores, cannot start/end with underscore, max 64 characters.
Examples: usr, api_key, org_member, sk_live, USR, ApiKey
API Reference
UPLID[PREFIX]
uid = UPLID.generate("usr") # Generate new
uid = UPLID.from_string("usr_0M3xL9kQ7vR2nP5wY1jZ4c", "usr") # Parse
uid.prefix # "usr"
uid.uid # UUID object
uid.base62_uid # "0M3xL9kQ7vR2nP5wY1jZ4c"
uid.datetime # When created (from UUIDv7)
uid.timestamp # Unix timestamp
factory(UPLIDType) / parse(UPLIDType)
UserId = UPLID[Literal["usr"]]
UserIdFactory = factory(UserId) # For Pydantic default_factory
parse_user_id = parse(UserId) # For manual parsing, raises UPLIDError
UPLIDType
Protocol for functions accepting any UPLID:
def log_entity(id: UPLIDType) -> None:
print(f"{id.prefix} created at {id.datetime}")
uplid_column / uplid_field
from uplid.sqlalchemy import uplid_column, uplid_field
# SQLAlchemy
id: Mapped[UserId] = uplid_column(UserId, primary_key=True)
# SQLModel
id: UserId = uplid_field(UserId, default_factory=factory(UserId), primary_key=True)
Credits
Created by ZVS
License
MIT
Metadata
Release files for uplid 1.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| uplid-1.2.0.tar.gz | 9.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| uplid-1.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 19.6 kB
Release files / uplid-1.2.0.tar.gz
| Download URL | uplid-1.2.0.tar.gz |
|---|---|
| Size | 9.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e5cb34b661f10fbe393a8a8873ac28b4028c3a49b8a8ad8d119c6f8eeb035e74
|
|
BLAKE2b-256 checksum How to use checksums |
e89b5eaaee81da85625150e2379469139002e97b7c2f54dcd01b6b7522c8680a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / uplid-1.2.0-py3-none-any.whl
| Download URL | uplid-1.2.0-py3-none-any.whl |
|---|---|
| Size | 10.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2df980717a7a3a83a2f80b659df50dd9bce9374fe2850432b0c4b27deab9ab32
|
|
BLAKE2b-256 checksum How to use checksums |
cb52ecda7a88cc06a71c57f241ac2cbdebf20c2abf0ab87e01907e426c83c9d7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|