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

# Generate a thumbnail and a GIF in one chained request

> Produce a thumbnail JPG and a preview GIF from the same video in a single chained request using Rendi's FFmpeg API. Node.js, Python, and cURL examples.

Produce a thumbnail and a short preview GIF in a single chained request. Chained commands run sequentially on the same input, and outputs from earlier commands can feed later ones — cheaper than two separate calls.

## Code

<Tabs>
  <Tab title="Node.js">
    ```js theme={null}
    const API_KEY = process.env.RENDI_API_KEY;

    const submit = await fetch(
      "https://api.rendi.dev/v1/run-chained-ffmpeg-commands",
      {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          "X-API-KEY": API_KEY,
        },
        body: JSON.stringify({
          input_files: {
            in_1: "https://storage.rendi.dev/sample/sample.avi",
          },
          output_files: {
            out_1: "thumbnail.jpg",
            out_2: "preview.gif",
          },
          ffmpeg_commands: [
            "-i {{in_1}} -ss 00:17 -vframes 1 {{out_1}}",
            "-i {{in_1}} -vf \"select='lte(t,60)*gt(trunc(t/10),trunc(prev_t/10))',setpts='PTS*0.1',scale=trunc(oh*a/2)*2:80:force_original_aspect_ratio=decrease,pad=trunc(oh*a/2)*2:80:-1:-1\" -an -vsync vfr {{out_2}}",
          ],
        }),
      },
    );
    const { command_id } = await submit.json();

    while (true) {
      const res = await fetch(
        `https://api.rendi.dev/v1/commands/${command_id}`,
        {
          headers: { "X-API-KEY": API_KEY },
        },
      );
      const data = await res.json();
      if (data.status === "SUCCESS") {
        console.log("Thumbnail:", data.output_files.out_1.storage_url);
        console.log("GIF:", data.output_files.out_2.storage_url);
        break;
      }
      if (data.status === "FAILED") throw new Error("Command failed");
      await new Promise((r) => setTimeout(r, 2000));
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import os
    import time
    import requests

    API_KEY = os.environ["RENDI_API_KEY"]
    BASE = "https://api.rendi.dev/v1"
    headers = {"X-API-KEY": API_KEY}

    submit = requests.post(
        f"{BASE}/run-chained-ffmpeg-commands",
        headers=headers,
        json={
            "input_files": {"in_1": "https://storage.rendi.dev/sample/sample.avi"},
            "output_files": {"out_1": "thumbnail.jpg", "out_2": "preview.gif"},
            "ffmpeg_commands": [
                "-i {{in_1}} -ss 00:17 -vframes 1 {{out_1}}",
                "-i {{in_1}} -vf \"select='lte(t,60)*gt(trunc(t/10),trunc(prev_t/10))',setpts='PTS*0.1',scale=trunc(oh*a/2)*2:80:force_original_aspect_ratio=decrease,pad=trunc(oh*a/2)*2:80:-1:-1\" -an -vsync vfr {{out_2}}",
            ],
        },
    )
    command_id = submit.json()["command_id"]

    while True:
        res = requests.get(f"{BASE}/commands/{command_id}", headers=headers).json()
        if res["status"] == "SUCCESS":
            print("Thumbnail:", res["output_files"]["out_1"]["storage_url"])
            print("GIF:", res["output_files"]["out_2"]["storage_url"])
            break
        if res["status"] == "FAILED":
            raise RuntimeError("Command failed")
        time.sleep(2)
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={null}
    curl --request POST \
      --url https://api.rendi.dev/v1/run-chained-ffmpeg-commands \
      --header 'Content-Type: application/json' \
      --header 'X-API-KEY: <api-key>' \
      --data '{
        "input_files": {"in_1": "https://storage.rendi.dev/sample/sample.avi"},
        "output_files": {"out_1": "thumbnail.jpg", "out_2": "preview.gif"},
        "ffmpeg_commands": [
          "-i {{in_1}} -ss 00:17 -vframes 1 {{out_1}}",
          "-i {{in_1}} -vf \"select='\''lte(t,60)*gt(trunc(t/10),trunc(prev_t/10))'\'',setpts='\''PTS*0.1'\'',scale=trunc(oh*a/2)*2:80:force_original_aspect_ratio=decrease,pad=trunc(oh*a/2)*2:80:-1:-1\" -an -vsync vfr {{out_2}}"
        ]
      }'

    curl --request GET \
      --url https://api.rendi.dev/v1/commands/<command_id> \
      --header 'X-API-KEY: <api-key>'
    ```
  </Tab>
</Tabs>

## How the FFmpeg commands work

**Command 1 (thumbnail):** `-i {{in_1}} -ss 00:17 -vframes 1 {{out_1}}` — grab a single frame at 17 s.

**Command 2 (GIF):**

* `select='lte(t,60)*gt(trunc(t/10),trunc(prev_t/10))'` — keep one frame every 10 s, up to the 60 s mark (6 frames total)
* `setpts='PTS*0.1'` — speed up playback by 10× so the GIF plays fast
* `scale=...:80:force_original_aspect_ratio=decrease,pad=...:80:-1:-1` — scale to 80 px tall, letterbox width
* `-an` — drop audio
* `-vsync vfr` — variable frame rate for the speed-up

## Response

```json theme={null}
{
  "command_id": "963b85e1-...",
  "status": "SUCCESS",
  "output_files": {
    "out_1": {
      "file_id": "5a978607-...",
      "size_mbytes": 0.024,
      "file_type": "image",
      "file_format": "jpg",
      "storage_url": "https://storage.rendi.dev/files/.../thumbnail.jpg",
      "width": 854,
      "height": 480
    },
    "out_2": {
      "file_id": "3f51e56e-...",
      "size_mbytes": 0.300,
      "file_type": "image",
      "file_format": "gif",
      "storage_url": "https://storage.rendi.dev/files/.../preview.gif",
      "width": 142,
      "height": 80
    }
  },
  "command_type": "FFMPEG_CHAINED_COMMANDS"
}
```

## Related

* [Generate a thumbnail](/examples/generate-thumbnail)
* [Generate multiple thumbnails](/examples/generate-multiple-thumbnails)
* [run-chained-ffmpeg-commands API reference](/api-reference/endpoint/run-chained-ffmpeg-commands)
* [Cheatsheet — Create GIFs](https://github.com/rendi-api/ffmpeg-cheatsheet#create-gifs)
