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

> ## Agent Instructions
> The Deepshi API is an OpenAI-compatible gateway. Base URL: https://api.deepshi.ai/v1. API keys start with sk-bf- and go in the Authorization: Bearer header. Prefer the official OpenAI SDKs pointed at the Deepshi base URL. Model ids are clean with no provider prefix (e.g. deepshi-3.0, claude-opus-4.8, gpt-5.5). Chat and image requests are synchronous; video and music requests are asynchronous job APIs (create, then poll). Every synchronous response carries usage.cost.total_cost in USD. Do not reference the internal /api/* admin plane or virtual keys.

# Animate an image

> Generate a still image, then turn it into a video clip with the image-to-video API.

This recipe chains two APIs: [image generation](/capabilities/image-generation) creates a still, and [video generation](/capabilities/video-generation) animates it. You end up with a short `.mp4`, with a soundtrack if the video model supports audio.

The trick that connects them: the image API returns the image as base64, and the video API accepts a base64 `data:` URI in `input_reference`. No upload step, no hosting.

## The plan

<Steps>
  <Step title="Generate a still">
    `POST /v1/images/generations` returns the image in `data[0].b64_json`.
  </Step>

  <Step title="Create a video job from it">
    `POST /v1/videos` with `input_reference` set to a `data:` URI built from that base64. The prompt now describes the **motion**, not the scene.
  </Step>

  <Step title="Poll and download">
    Poll `GET /v1/videos/{id}` until `completed`, then download `videos[0].url`.
  </Step>
</Steps>

## Full script

<CodeGroup>
  ```python Python theme={null}
  import base64, time, requests

  BASE = "https://api.deepshi.ai/v1"
  headers = {"Authorization": "Bearer YOUR_DEEPSHI_API_KEY"}

  # 1. generate the still
  img = requests.post(f"{BASE}/images/generations", headers=headers, json={
      "model": "deepshi-banana-pro",
      "prompt": "a tiny paper boat on a rain-soaked street at dusk, cinematic light",
      "width": 1280,
      "height": 720,
  }).json()

  b64 = img["data"][0]["b64_json"]
  open("still.png", "wb").write(base64.b64decode(b64))
  print("saved still.png, cost:", img["usage"]["cost"]["total_cost"])

  # 2. animate it (the prompt describes motion, not the scene)
  job = requests.post(f"{BASE}/videos", headers=headers, json={
      "model": "veo3.1-fast",
      "prompt": "the boat drifts forward as rain ripples the water, camera slowly pulls back",
      "input_reference": f"data:image/png;base64,{b64}",
      "resolution": "720p",
      "duration": 4,
  }).json()

  # 3. poll, then download
  while job["status"] not in ("completed", "failed"):
      time.sleep(5)
      job = requests.get(f"{BASE}/videos/{job['id']}", headers=headers).json()

  if job["status"] == "failed":
      raise SystemExit(job.get("error"))

  url = job["videos"][0]["url"]
  open("clip.mp4", "wb").write(requests.get(url).content)
  print("saved clip.mp4")
  ```

  ```javascript JavaScript theme={null}
  import { writeFileSync } from "node:fs";

  const BASE = "https://api.deepshi.ai/v1";
  const headers = {
    Authorization: "Bearer YOUR_DEEPSHI_API_KEY",
    "Content-Type": "application/json",
  };

  // 1. generate the still
  const img = await (await fetch(`${BASE}/images/generations`, {
    method: "POST",
    headers,
    body: JSON.stringify({
      model: "deepshi-banana-pro",
      prompt: "a tiny paper boat on a rain-soaked street at dusk, cinematic light",
      width: 1280,
      height: 720,
    }),
  })).json();

  const b64 = img.data[0].b64_json;
  writeFileSync("still.png", Buffer.from(b64, "base64"));
  console.log("saved still.png, cost:", img.usage.cost.total_cost);

  // 2. animate it (the prompt describes motion, not the scene)
  let job = await (await fetch(`${BASE}/videos`, {
    method: "POST",
    headers,
    body: JSON.stringify({
      model: "veo3.1-fast",
      prompt: "the boat drifts forward as rain ripples the water, camera slowly pulls back",
      input_reference: `data:image/png;base64,${b64}`,
      resolution: "720p",
      duration: 4,
    }),
  })).json();

  // 3. poll, then download
  while (!["completed", "failed"].includes(job.status)) {
    await new Promise((r) => setTimeout(r, 5000));
    job = await (await fetch(`${BASE}/videos/${job.id}`, { headers })).json();
  }
  if (job.status === "failed") throw new Error(JSON.stringify(job.error));

  const buf = Buffer.from(await (await fetch(job.videos[0].url)).arrayBuffer());
  writeFileSync("clip.mp4", buf);
  console.log("saved clip.mp4");
  ```
</CodeGroup>

## Notes

* **Write the video prompt for motion.** The still already defines the scene. Describe what moves, and how the camera behaves.
* **Match the aspect ratios.** The still above is 1280x720 (16:9) to match the video's `resolution: "720p"`. A mismatched reference gets cropped or padded by the model.
* **Pick a video model that supports image-to-video.** Only models whose card on the [video models](/models/video-models) page lists image-to-video accept `input_reference`. `veo3.1-fast` also generates audio; `grok-imagine-video` is a cheaper option that also carries built-in sound.
* **Cost:** the image response reports its exact cost inline. The video is billed per second on completion; see [video pricing](/capabilities/video-generation#pricing).

## Next steps

<CardGroup cols={2}>
  <Card title="Image generation" icon="image" href="/capabilities/image-generation">
    All image parameters, models, and pricing.
  </Card>

  <Card title="Video generation" icon="clapperboard" href="/capabilities/video-generation">
    All video parameters, models, and pricing.
  </Card>
</CardGroup>
