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

# Python SDK

> Official Python SDK for the Framelane API.

## Installation

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

## Quick start

Upload local media via [`POST /v1/uploads`](/introduction/uploads) first, then pass the returned `source_url` below.

```python theme={null}
import os
import framelane

client = framelane.Framelane(api_key=os.environ["FRAMELANE_API_KEY"])

# Submit a render
render = client.renders.create(
    width=1920,
    height=1080,
    duration=10.0,
    frame_rate=30,
    output_format="mp4",
    elements=[
        {
            "type": "video",
            "id": "clip1",
            "source_url": os.environ["SOURCE_URL"],
            "time": 0,
            "in_point": 0,
            "out_point": 10,
        }
    ],
)

# Wait for completion
completed = client.renders.wait_for_completion(render.id)
print(completed.output.url)
```

## API coverage

| Resource           | Methods                                                              |
| ------------------ | -------------------------------------------------------------------- |
| `client.renders`   | `create`, `get`, `list`, `cancel`, `download`, `wait_for_completion` |
| `client.tasks`     | `create`, `get`, `list`, `cancel`, `download`, `wait_for_completion` |
| `client.webhooks`  | `create`, `get`, `list`, `update`, `delete`, `test`, `rotate_secret` |
| `client.api_keys`  | `create`, `list`, `revoke`                                           |
| `client.workspace` | `get`, `get_usage`, `update`, `delete`                               |

## Webhook signature verification

```python theme={null}
from framelane import verify_webhook_signature
from flask import Flask, request

app = Flask(__name__)

@app.post("/hooks/framelane")
def handle_webhook():
    is_valid = verify_webhook_signature(
        raw_body=request.data,
        signature=request.headers.get("Framelane-Signature"),
        secret=os.environ["FRAMELANE_WEBHOOK_SECRET"],
    )
    if not is_valid:
        return "Invalid signature", 400

    event = request.json
    # handle event...
    return "", 200
```

## Async support

```python theme={null}
import asyncio
import framelane

async def main():
    client = framelane.AsyncFramelane(api_key=os.environ["FRAMELANE_API_KEY"])
    render = await client.renders.create(...)
    completed = await client.renders.wait_for_completion(render.id)
    print(completed.output.url)

asyncio.run(main())
```
