> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pictify.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Python SDK

> Official Pictify SDK for Python

# Python SDK

The official Pictify SDK for Python provides a simple, Pythonic interface for the Pictify API — generate images, PDFs, and GIFs from raw HTML, live URLs, and reusable templates. It ships sync (`Pictify`) and async (`AsyncPictify`) clients with an identical surface.

## Installation

```bash theme={null}
pip install pictify
```

## Quick Start

```python theme={null}
from pictify import Pictify

client = Pictify(api_key="your-api-key")

# Render raw HTML to a PNG
image = client.render_html(html="<div style='font-size:48px;padding:40px'>Hello World</div>")
print(image.url)

# Render a reusable template
result = client.render("XL13XACH2V", variables={"name": "Ada", "company": "Pictify"})
print(result.url)  # results[0].url
```

## Async Usage

```python theme={null}
import asyncio
from pictify import AsyncPictify

async def main():
    async with AsyncPictify(api_key="your-api-key") as client:
        image = await client.render_html(html="<div>Hello World</div>")
        print(image.url)

asyncio.run(main())
```

## Configuration

The client is constructed with a keyword `api_key`. Every request is sent with an `Authorization: Bearer <API_KEY>` header.

```python theme={null}
client = Pictify(
    api_key="your-api-key",
    base_url="https://api.pictify.io",  # optional: API base URL (default: https://api.pictify.io)
    timeout=30.0,                       # optional: request timeout in seconds (default: 30)
    max_retries=3,                      # optional: retries on 5xx / network errors (default: 3)
)
```

<Warning>
  Keep your API key secret. Read it from an environment variable (e.g. `os.environ["PICTIFY_API_KEY"]`) and never expose it in client-side code or public repositories.
</Warning>

Both clients are context managers and expose `close()`:

```python theme={null}
with Pictify(api_key="your-api-key") as client:
    ...

async with AsyncPictify(api_key="your-api-key") as client:
    ...
```

## Render an Image from HTML

`render_html(html, *, css=None, width=None, height=None, selector=None, format=None)` — `POST /image`. Returns an `ImageResult` with `url`, `id`, and `created_at`.

```python theme={null}
image = client.render_html(
    html="<div style='padding:40px'>Hello</div>",
    css="div { color: blue; }",   # optional — inlined into a <style> tag before the HTML
    width=1200,                    # optional (default: 1280)
    height=630,                    # optional (default: 720)
    selector="#card",             # optional — crop to a specific element
    format="png",                 # optional: 'png' | 'jpg' | 'jpeg' | 'webp' | 'pdf' (default: png)
)
print(image.url, image.id, image.created_at)
```

The `/image` endpoint accepts a single `html` field, so any `css` you pass is injected into a `<style>` tag prepended to the HTML. `format` is mapped to the endpoint's `fileExtension`.

## Screenshot a Live URL

`render_url(url, *, width=None, height=None, selector=None, format=None)` — `POST /image` with `url`. Returns an `ImageResult`.

```python theme={null}
image = client.render_url(url="https://example.com", width=1280, height=720)
print(image.url)
```

## Render a Template

`render(template_id, *, variables=None, format=None, quality=None, width=None, height=None, layout=None, layouts=None)` — `POST /templates/:uid/render`.

The response is a `results` envelope; `result.url` is a convenience accessor for `results[0].url`.

```python theme={null}
result = client.render(
    "XL13XACH2V",
    variables={"name": "Ada", "company": "Pictify"},
    format="png",   # optional: 'png' | 'jpg' | 'jpeg' | 'webp' | 'pdf' (default: png)
    quality=0.9,    # optional render quality, 0.1-1.0 (default: 0.9)
    width=1200,     # optional
    height=630,     # optional
)
print(result.url)
print(result.results[0])  # RenderResultItem: layout, url, width, height, format, name, id, created_at
```

### Render a Specific Layout

```python theme={null}
result = client.render(
    "XL13XACH2V",
    variables={"name": "Ada"},
    layout="square",
)
```

### Render Multiple Layouts

`render_layouts(template_id, layouts, *, variables=None, format=None, quality=None, width=None, height=None)` — `POST /templates/:uid/render` with `layouts` (max 20).

Each successful layout appears in `results`; missing/invalid layouts appear in `errors`.

```python theme={null}
result = client.render_layouts(
    "XL13XACH2V",
    layouts=["default", "square", "story"],
    variables={"name": "Ada"},
)
for item in result.results:
    print(item.layout, item.url, f"{item.width}x{item.height}")
for err in result.errors:
    print("failed:", err.layout, err.error)
```

## Render an Animated GIF

`render_gif(*, html=None, url=None, template_id=None, variables=None, width=None, height=None, quality=None)` — `POST /gif`.

Provide exactly one source: `html`, `url`, or `template_id`. The `{gif: {...}}` envelope is flattened.

```python theme={null}
gif = client.render_gif(
    html="<style>@keyframes p{0%{opacity:.2}50%{opacity:1}100%{opacity:.2}}div{animation:p 2s infinite}</style><div>Hi</div>",
    width=400,         # optional (default: 800)
    height=200,        # optional (default: 600)
    quality="medium",  # optional: 'low' | 'medium' | 'high' (default: medium)
)
print(gif.url, gif.uid, gif.animation_length)

# From a template
gif = client.render_gif(template_id="XL13XACH2V", variables={"name": "Ada"})

# From a live URL
gif = client.render_gif(url="https://example.com/animated-page")
```

<Note>
  The source HTML/URL must contain motion (e.g. a CSS animation). Static content produces no frames and returns a render error (HTTP 422).
</Note>

## Batch Rendering (async)

`render_batch(template_id, variable_sets, *, format=None, quality=None, concurrency=None, layout=None, layouts=None)` — `POST /templates/:uid/batch-render`.

Submitting returns immediately (HTTP 202) with a `batch_id`. Poll `get_batch_results` for progress.

```python theme={null}
job = client.render_batch(
    "XL13XACH2V",
    variable_sets=[
        {"name": "Ada", "company": "Pictify"},
        {"name": "Grace", "company": "Pictify"},
    ],  # max 100 per batch
    format="png",     # optional
    quality=0.9,      # optional, 0.1-1.0
    concurrency=5,    # optional, 1-10 (default: 5)
)
print(job.batch_id, job.status, job.total_items)
```

`get_batch_results(batch_id)` — `GET /templates/batch/:batchId/results`.

```python theme={null}
status = client.get_batch_results(job.batch_id)
print(status.status, status.completed_items, "/", status.total_items)
for item in status.results:
    print(item.index, item.success, item.variables)  # no URLs (see webhook)
```

<Warning>
  **Rendered URLs are not returned by the poll endpoint.** Per-item records carry `{index, success, variables}` (and `error` on failures). Final image URLs are delivered via the `render.completed` webhook — subscribe to webhooks to collect batch output.
</Warning>

## Templates

### Get a Template

`get_template(template_id)` — `GET /templates/:uid`.

```python theme={null}
template = client.get_template("XL13XACH2V")
print(template.uid, template.name)
print([v.name for v in (template.variable_definitions or [])])
```

### List Templates

`list_templates(*, page=None, limit=None, sort=None)` — `GET /templates`. Returns a `ListTemplatesResult` with `templates` and `pagination`.

```python theme={null}
result = client.list_templates(page=1, limit=20, sort="newest")
for t in result.templates:
    print(t.uid, t.name)
print(result.pagination.total, result.pagination.has_next)
```

### Create a Template

`create_template(html, *, name=None, width=None, height=None, variable_definitions=None, output_format=None)` — `POST /templates`. Variables are auto-discovered from `{{variableName}}` tokens.

```python theme={null}
template = client.create_template(
    html="<div>Hi {{first_name}}</div>",
    name="Welcome Card",
    width=600,
    height=200,
    output_format="image",  # optional: 'image' | 'pdf'
)
print(template.uid)
```

## Error Handling

```python theme={null}
from pictify import (
    Pictify,
    PictifyError,
    AuthenticationError,
    TemplateNotFoundError,
    RateLimitError,
    QuotaExceededError,
    RenderError,
    ServerError,
)

client = Pictify(api_key="your-api-key")

try:
    result = client.render("XL13XACH2V", variables={"name": "Ada"})
except AuthenticationError:
    print("Invalid API key")
except TemplateNotFoundError:
    print("Template not found")
except RateLimitError as e:
    print(f"Rate limited. Retry after: {e.retry_after}s")
except QuotaExceededError:
    print("Render quota exceeded")
except RenderError as e:
    print(f"Render/validation failed: {e.message}; field errors: {e.errors}")
except ServerError as e:
    print(f"Server error: {e.message}")
except PictifyError as e:
    print(f"Error: {e.message}")
```

Status → error mapping: `401 → AuthenticationError`, `402 → QuotaExceededError`, `404 → TemplateNotFoundError`, `422 → RenderError` (with field-level `errors`), `429 → QuotaExceededError` when `code == "quota_exceeded"` else `RateLimitError`, other 4xx → `RenderError`, `5xx → ServerError`. Only 5xx and network errors are retried.

## Type Hints

The SDK ships full type hints and Pydantic result models:

```python theme={null}
from pictify import (
    Pictify,
    AsyncPictify,
    ImageResult,
    RenderResult,
    RenderResultItem,
    GifRenderResult,
    BatchRenderResult,
    BatchResults,
    Template,
    ListTemplatesResult,
    ImageFormat,
    GifQuality,
)
```

## Django Integration

```python theme={null}
# views.py
from django.conf import settings
from django.http import HttpResponseRedirect
from pictify import Pictify

client = Pictify(api_key=settings.PICTIFY_API_KEY)

def og_image(request, slug):
    post = Post.objects.get(slug=slug)

    result = client.render(
        "og-image-template",
        variables={
            "title": post.title,
            "description": post.excerpt,
            "author": post.author.name,
        },
    )

    return HttpResponseRedirect(result.url)
```

## API Reference

See the [API Reference](/api-reference/overview) for full endpoint documentation.
