# Render videos in Python

> Render a video template as an MP4 and follow its progress with the Python SDK.

Canonical: https://bannerify.co/docs/sdk/python/apis/render-video

A video template renders as an MP4. Each second of the video counts as one generation, and as two at 60 fps. [Generate videos](https://bannerify.co/docs/essentials/generate-videos) explains how to design one.

`render_video` and `render_and_wait_video` take a template id and keyword arguments.

<ParamField body="template_id" type="str" required>
  The id of a video template, for example `tpl_xxx`.
</ParamField>

<ParamField body="modifications" type="list[dict]">
  The values to put into the template's layers. A video layer takes a `src`, and an audio track takes a `src` with its name. [Modifications](https://bannerify.co/docs/essentials/modifications) lists the fields each layer accepts.
</ParamField>

## Render and wait for the URL

`render_and_wait_video` returns `{"result": url}` or `{"error": {...}}`.

```python
result = client.render_and_wait_video(
    "tpl_xxx",
    modifications=[
        {"name": "headline", "text": "Summer sale"},
        {"name": "music", "src": "https://example.com/song.mp3"},
    ],
)

if "error" in result:
    raise RuntimeError(result["error"]["message"])

print(result["result"])
```

## Follow the progress

`render_video` yields event dicts. The last event is `done` or `error`.

```python
for event in client.render_video("tpl_xxx", modifications=[{"name": "headline", "text": "Summer sale"}]):
    if event["type"] == "queued":
        print(f"{event['ahead']} videos before this one")
    elif event["type"] == "progress":
        print(f"{event['progress']:.0%}")
    elif event["type"] == "done":
        print(event["url"])
    elif event["type"] == "error":
        print(event["error"]["message"])
```

| Event | Fields |
| --- | --- |
| `created` | `id`: the id of the video, for example `vid_xxx` |
| `queued` | `ahead`: the number of videos before this one |
| `progress` | `frame`, `totalFrames`, and `progress` from 0 to 1 |
| `done` | `url`: the URL of the MP4 |
| `error` | `error`: `code`, `message`, and `requestId` |

A refused request, such as an invalid API key or a used-up quota, gives one `error` event. The client timeout applies only to the connection, because a video can wait in line for minutes.

## Reconnect after a dropped connection

The video renders to its end, even when the connection drops. Then `render_video` reads the same video again by the id from the `created` event, and it continues to yield events. It never sends the render again, so you do not pay for a second video.

| Attempt | Wait before it |
| --- | --- |
| 1 | 1 second |
| 2 | 2 seconds |
| 3 | 4 seconds |
| 4 | 8 seconds |

When the attempts run out, or the connection drops before the `created` event, the last event is an `error` with the code `TIMEOUT_ERROR`. `render_and_wait_video` reconnects in the same way.

## Start a video without waiting

`create_video` starts the video and returns its id at once. It does not wait for the render. Then read the video with `get_video` or `watch_video`.

```python
result = client.create_video("tpl_xxxxxxxxx", modifications=[{"name": "title", "text": "Hi"}])

if "error" in result:
    raise RuntimeError(result["error"]["message"])

print(result["result"]["id"])  # vid_xxx, with the status queued
```

`create_video` sends the render only once. A second request renders a second video.

## Read a video again

Keep the id from the `created` event. `get_video` returns the status of the video, and its URL when it is done.

```python
result = client.get_video("vid_xxx")

if "error" in result:
    raise RuntimeError(result["error"]["message"])

video = result["result"]
print(video["status"], video.get("url"))
```

`status` is `queued`, `rendering`, `done`, or `failed`. A failed video has an `error` string. An unknown id gives an error with the code `NOT_FOUND`.

`watch_video` yields the events of the video again, from the latest one. It has no `created` event, and it reconnects as `render_video` does.

```python
for event in client.watch_video("vid_xxx"):
    if event["type"] == "done":
        print(event["url"])
```

## Related

- [Previous: Create images and PDFs in Python](https://bannerify.co/docs/sdk/python/apis/create-image)
- [Next: Signed URLs in Python](https://bannerify.co/docs/sdk/python/apis/signed-url)
- [Documentation index](https://bannerify.co/docs/llms.txt)
