Metadata-Version: 2.4
Name: createrington-skin-api
Version: 1.1.0
Summary: Official Python client for the Createrington Skin API.
Project-URL: Homepage, https://api.createrington.com
Project-URL: Repository, https://gitea.matejhoz.com/matejhozlar/skin-api
Author: Matej Hozlar
License: Copyright (c) 2026 Matej Hozlar. All Rights Reserved.
        
        This software and its accompanying source code, documentation, assets, and any
        related materials (the "Software") are the proprietary and confidential
        property of Matej Hozlar ("the Author") and are protected by copyright law and
        international treaties.
        
        NO LICENSE IS GRANTED.
        
        No part of the Software may be copied, reproduced, modified, merged,
        published, distributed, sublicensed, sold, leased, rented, publicly displayed,
        publicly performed, transmitted, reverse engineered, decompiled, disassembled,
        or otherwise exploited in any form or by any means, in whole or in part,
        without the prior express written permission of the Author.
        
        Access to the Software, including via source repositories, build artifacts, or
        deployed services, does not grant any license or right of use beyond what has
        been explicitly authorized in writing by the Author.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN
        ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
        WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
        
        All rights not expressly granted herein are reserved by the Author.
License-File: LICENSE
Keywords: api-client,createrington,minecraft,render,skin
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: httpx>=0.27
Provides-Extra: dev
Requires-Dist: mypy>=1.8; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Description-Content-Type: text/markdown

# 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.

```sh
pip install createrington-skin-api
```

> Access is invite-only. Request an API key at https://api.createrington.com.

## Quickstart

```python
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

```python
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

```python
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`

```python
client.render(
    pose,                  # a KNOWN_POSES 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
    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`.

`pose` accepts any string, so server-side poses added after this release work
without an SDK upgrade. The bundled `KNOWN_POSES` tuple and `KnownPose` type
cover the poses known at publish time; fetch `GET /v1/poses` directly if you
need the live catalogue with descriptions.

```python
from createrington_skin_api import KNOWN_POSES, KnownPose
```

`random_pose()` returns a uniformly random pose name from `KNOWN_POSES`:

```python
from createrington_skin_api import random_pose

png = client.render(random_pose(), uuid="069a79f444e94726a5befca90e38aaf5")
```

## Errors

Every non-2xx response (and network/timeout failures) raises `SkinApiError`:

```python
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.

## License

UNLICENSED. See repository for terms.
