createrington-skin-api
Official Python client for the Createrington Skin API. Renders Minecraft player skins into named poses and returns PNG bytes. Sync and async clients, fully typed.
pip install createrington-skin-api
Access is invite-only. Request an API key at https://api.createrington.com.
Quickstart
from createrington_skin_api import SkinApiClient
client = SkinApiClient(api_key="sk_...") # or set SKIN_API_KEY
# Render a known pose for a Minecraft account by UUID.
png = client.render("wave", uuid="069a79f444e94726a5befca90e38aaf5")
# `png` is `bytes` of a PNG image.
with open("notch-waving.png", "wb") as f:
f.write(png)
Async
import asyncio
from createrington_skin_api import AsyncSkinApiClient
async def main() -> None:
async with AsyncSkinApiClient(api_key="sk_...") as client:
png = await client.render("wave", username="Notch", slim=True)
with open("notch-waving.png", "wb") as f:
f.write(png)
asyncio.run(main())
Client
SkinApiClient(
api_key=None, # required; falls back to the SKIN_API_KEY env var
base_url="https://api.createrington.com",
timeout=30.0, # seconds
retries=2, # retries 429/502/503/504 and network errors
user_agent="createrington-skin-api",
)
AsyncSkinApiClient takes the same arguments. Both are usable as context
managers (with / async with) and expose close() / aclose() for
explicit cleanup of the underlying connection pool.
render
client.render(
pose, # a pose name (e.g. "wave"), or any pose string
*,
# exactly one skin source:
uuid=None, # Mojang UUID, resolved server-side
username=None, # Mojang username, resolved server-side
skin_url=None, # public URL to a 64x64 PNG
skin_base64=None, # base64-encoded 64x64 PNG (data URL prefix optional)
png=None, # raw 64x64 PNG bytes, sent as multipart/form-data
# options:
slim=None, # override slim/Alex arm geometry; default uses skin metadata
outline=False, # draw an outline around the skin; default off
width=None, # default 400 (64..2048)
height=None, # default 600 (64..2048)
) -> bytes
Exactly one skin source must be supplied; passing none or more than one
raises ValueError.
Poses exposes every pose known to the SDK at publish time as a named
constant, so you can reference one by name instead of a bare string:
from createrington_skin_api import Poses
png = client.render(Poses.wave, uuid="069a79f444e94726a5befca90e38aaf5")
pose accepts any string, so server-side poses added after this release work
without an SDK upgrade; fetch GET /v1/poses directly if you need the live
catalogue with descriptions.
random_pose() returns a uniformly random known pose name:
from createrington_skin_api import random_pose
png = client.render(random_pose(), uuid="069a79f444e94726a5befca90e38aaf5")
avatar
client.avatar(
*,
# exactly one skin source (same as render):
uuid=None, # Mojang UUID, resolved server-side
username=None, # Mojang username, resolved server-side
skin_url=None, # public URL to a 64x64 PNG
skin_base64=None, # base64-encoded 64x64 PNG (data URL prefix optional)
png=None, # raw 64x64 PNG bytes, sent as multipart/form-data
# options:
size=None, # output edge length in px; default 64 (8..512), square
overlay=None, # composite the hat layer; on by default
) -> bytes
Returns a flat 2D front-view avatar: a square PNG of the skin's face with the
hat layer composited on top. Exactly one skin source must be supplied, the same
way as render; passing none or more than one raises ValueError. overlay
is on by default and the request omits the parameter unless you pass
overlay=False.
png = client.avatar(uuid="069a79f444e94726a5befca90e38aaf5", size=128)
The async client exposes await client.avatar(...) with the same arguments.
resolve
client.resolve(
*,
# exactly one identifier:
uuid=None, # player UUID, dashed or compact
username=None, # Minecraft username, case-insensitive
) -> ResolvedPlayer
Resolves a player identity in either direction: pass uuid to get the current
username, or username to get the UUID. Exactly one identifier must be
supplied; passing none or both raises ValueError. Returns a ResolvedPlayer
dataclass: uuid is always the canonical dashed lowercase form and username
carries the canonical casing (None only when a degraded fallback provider
could not supply the name). An unknown player raises SkinApiError with
code == "not_found".
player = client.resolve(username="Notch")
print(player.uuid) # "069a79f4-44e9-4726-a5be-fca90e38aaf5"
print(player.username) # "Notch"
Lookups share the server's resolution cache, so a recent name change can take up to a day to appear. Resolutions do not count toward the image volume quota.
The async client exposes await client.resolve(...) with the same arguments.
Errors
Every non-2xx response (and network/timeout failures) raises SkinApiError:
from createrington_skin_api import SkinApiError
try:
client.render("wave", uuid="bad-uuid")
except SkinApiError as err:
print(err.code, err.status, err)
if err.code == "rate_limited" and err.retry_after_ms:
... # back off and retry
err.code is one of "bad_request", "unauthorized", "forbidden",
"not_found", "conflict", "unsupported_media_type", "rate_limited",
"internal", "render_failed", "upstream_unavailable", "timeout",
"aborted", "network_error", "unknown". err.status is the HTTP status
(or 0 for network/timeout failures). err.retry_after_ms is populated on
429 responses when the server reports it.
The client retries 429, 502, 503, 504, and network errors up to
retries times with exponential backoff; 429 responses honour the server's
retryAfterMs when present.
Building
pip install -e ".[dev]"
pytest
mypy
_poses.py is generated from the published OpenAPI document (fetched live, not
committed):
python scripts/generate_poses.py
render accepts any pose string, so a new server-side pose works without an
SDK change; Poses and KNOWN_POSES only provide names known at build time.
Contributing
Issues and pull requests are welcome. By submitting a contribution you agree it is licensed under the project's Apache-2.0 terms (section 5 of the license); no separate CLA is required.
License
Apache-2.0. See LICENSE.
Release files for createrington-skin-api 2.7.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 | |
|---|---|---|---|
| createrington_skin_api-2.7.0.tar.gz | 20.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| createrington_skin_api-2.7.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 40.8 kB
Release files / createrington_skin_api-2.7.0.tar.gz
| Download URL | createrington_skin_api-2.7.0.tar.gz |
|---|---|
| Size | 20.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5a03f597d8c50297e6cd06d3aed2c736be517d65df1b00055bee6d5d6cdfd430
|
|
BLAKE2b-256 checksum How to use checksums |
d92444e5e3ad904e1890a25484f146c3fa3fc3c1463225d1a13261380d95dd2e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.13
|
Release files / createrington_skin_api-2.7.0-py3-none-any.whl
| Download URL | createrington_skin_api-2.7.0-py3-none-any.whl |
|---|---|
| Size | 20.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ea5134c22230d358529ff41cb3027bb3953e6771d4fcee60d081410980a7882e
|
|
BLAKE2b-256 checksum How to use checksums |
94cb713e93fd3adc60d9f9f17765b5be65e0c086211c08865a63e20cb8237a18
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.13
|