# Speed Painting API Reference

{% hint style="info" %}
**Good to know:** A quick start guide can be good to help folks get up and running with your API in a few steps. Some people prefer diving in with the basics rather than meticulously reading every page of documentation!
{% endhint %}

## Get your API keys

Your `API` requests are authenticated using `API` keys. Any request that doesn't include an `API` key will return an `error`.

You can generate an `API` key from your user `dashboard` in [Miragic](https://miragic.ai/) website anytime.

<figure><img src="https://3797193121-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Ft2ubAKYPEaJXBQZaRNa5%2Fuploads%2FQRG37HdbiuNEPYckUX7g%2FScreenshot%202025-10-10%20084345.png?alt=media&#x26;token=9b926638-0508-4358-8eee-6a9913c50034" alt=""><figcaption></figcaption></figure>

## Authentication

All requests must include the `X-API-Key` header containing your assigned `API` key.&#x20;

{% tabs %}
{% tab title="cURL" %}

```bash
curl -X POST "https://backend.miragic.ai/api/v1/speed-painting/create" \
  -H "X-API-Key: YOUR_API_KEY" \
  -F "fps=30" \
  -F "quality=hd" \
  -F "sequence=auto" \
  -F "hand_style=1" \
  -F "image=@path/to/image.jpg"
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
import fs from "fs";
import FormData from "form-data";
import fetch from "node-fetch";

const apiKey = "YOUR_API_KEY";
const baseUrl = "https://backend.miragic.ai";

async function speedPainting() {
  const url = `${baseUrl}/api/v1/speed-painting/create`;

  const formData = new FormData();
  formData.append("fps", "30");
  formData.append("quality", "hd");
  formData.append("sequence", "auto");
  formData.append("hand_style", "1");
  formData.append("image", fs.createReadStream("path/to/image.jpg"));

  const response = await fetch(url, {
    method: "POST",
    headers: {
      "X-API-Key": apiKey,
      ...formData.getHeaders(),
    },
    body: formData,
  });

  const result = await response.text();
  console.log(result);
}

speedPainting();
```

{% endtab %}

{% tab title="Python" %}

```python
import requests
import time

api_key = 'YOUR_API_KEY'   # ← Replace with your actual API key
base_url = 'https://backend.miragic.ai'

def speedpainting():
    url = f'{base_url}/api/v1/speed-painting/create'

    data = {
        'fps': '30',        # Frames per second (30 or 60)
        'quality': 'hd',    # Can be 'sd' or 'hd'
        'sequence': 'auto', # How the drawing animation sequence is generated
        'hand_style': '1'   # Hand style (likely options: 0 = none, 1 = realistic, etc.)
    }

    files = [
        ('image', ('image.jpg', open('path/to/image.jpg', 'rb'), 'image/jpeg'))
    ]
    headers = {'X-API-Key': api_key}
    response = requests.request("POST", url, headers=headers, data=data, files=files)
    print(response.text)

if __name__ == '__main__':
    speedpainting()
```

{% endtab %}
{% endtabs %}

## How To Create Speed Painting Task

<mark style="background-color:green;">POST</mark>   `/api/v1/speed-painting/create`

This `API` starts the `Speed Painting` process by creating a task that generates an hand-drawn artworks video.

{% hint style="info" %}
Processing Information

* Tasks are processed asynchronously in the background
* Progress can be monitored using the `Get Task Status API`
* The final result will be a high-quality video with audio
  {% endhint %}

### Request

<table><thead><tr><th width="119">Parameter</th><th width="85">Type</th><th width="113">Required</th><th>Description</th></tr></thead><tbody><tr><td>image</td><td>File</td><td>Yes</td><td>Input image</td></tr><tr><td>audio_file</td><td>File</td><td>Optional</td><td>Input audio</td></tr><tr><td>quality</td><td>String</td><td>Yes</td><td>This value can be <code>hd</code> or <code>sd</code></td></tr><tr><td>sequence</td><td>String</td><td>Yes</td><td>How the drawing animation sequence is generated. the value can be <code>auto</code> or <code>vertical</code></td></tr><tr><td>fps</td><td>Number</td><td>Yes</td><td>Frames per second: This can be <code>30</code> or <code>60</code></td></tr><tr><td>hand_style</td><td>Number</td><td>Yes</td><td>The value can be <code>0</code>, <code>1</code>, <code>2</code>, <code>3</code>, <code>4</code></td></tr></tbody></table>

<mark style="color:green;">**Request Example (Without audio input)**</mark>

{% tabs %}
{% tab title="cURL" %}

```bash
curl -X POST "https://backend.miragic.ai/api/v1/speed-painting/create" \
  -H "X-API-Key: YOUR_API_KEY" \
  -F "fps=30" \
  -F "quality=hd" \
  -F "sequence=auto" \
  -F "hand_style=1" \
  -F "image=@path/to/image.jpg"
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
import fs from "fs";
import FormData from "form-data";
import fetch from "node-fetch";

const apiKey = "YOUR_API_KEY";
const baseUrl = "https://backend.miragic.ai";

async function speedPainting() {
  const url = `${baseUrl}/api/v1/speed-painting/create`;

  const formData = new FormData();
  formData.append("fps", "30");
  formData.append("quality", "hd");
  formData.append("sequence", "auto");
  formData.append("hand_style", "1");
  formData.append("image", fs.createReadStream("path/to/image.jpg"));

  const response = await fetch(url, {
    method: "POST",
    headers: {
      "X-API-Key": apiKey,
      ...formData.getHeaders(),
    },
    body: formData,
  });

  const result = await response.text();
  console.log(result);
}

speedPainting();
```

{% endtab %}

{% tab title="Python" %}

```python
import requests
import time

api_key = 'YOUR_API_KEY'   # ← Replace with your actual API key
base_url = 'https://backend.miragic.ai'

def speedpainting():
    url = f'{base_url}/api/v1/speed-painting/create'

    data = {
        'fps': '30',        # Frames per second (30 or 60)
        'quality': 'hd',    # Can be 'sd' or 'hd'
        'sequence': 'auto', # How the drawing animation sequence is generated
        'hand_style': '1'   # Hand style (likely options: 0 = none, 1 = realistic, etc.)
    }

    files = [
        ('image', ('image.jpg', open('path/to/image.jpg', 'rb'), 'image/jpeg'))
    ]
    headers = {'X-API-Key': api_key}
    response = requests.request("POST", url, headers=headers, data=data, files=files)
    print(response.text)

if __name__ == '__main__':
    speedpainting()
```

{% endtab %}
{% endtabs %}

<mark style="color:green;">**Request Example (With audio input)**</mark>

{% tabs %}
{% tab title="cURL" %}

```bash
curl -X POST "https://backend.miragic.ai/api/v1/speed-painting/create" \
  -H "X-API-Key: YOUR_API_KEY" \
  -F "fps=30" \
  -F "quality=hd" \
  -F "sequence=auto" \
  -F "hand_style=1" \
  -F "image=@path/to/image.jpg"
  -F "audio_file=@path/to/audio.mp3"
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
import fs from "fs";
import FormData from "form-data";
import fetch from "node-fetch";

const apiKey = "YOUR_API_KEY";
const baseUrl = "https://backend.miragic.ai";

async function speedPainting() {
  const url = `${baseUrl}/api/v1/speed-painting/create`;

  const formData = new FormData();
  formData.append("fps", "30");
  formData.append("quality", "hd");
  formData.append("sequence", "auto");
  formData.append("hand_style", "1");
  formData.append("image", fs.createReadStream("path/to/image.jpg"));
  formData.append("audio_file", fs.createReadStream("path/to/audio.mp3"));

  const response = await fetch(url, {
    method: "POST",
    headers: {
      "X-API-Key": apiKey,
      ...formData.getHeaders(),
    },
    body: formData,
  });

  const result = await response.text();
  console.log(result);
}

speedPainting();
```

{% endtab %}

{% tab title="Python" %}

```python
import requests
import time

api_key = 'YOUR_API_KEY'   # ← Replace with your actual API key
base_url = 'https://backend.miragic.ai'

def speedpainting():
    url = f'{base_url}/api/v1/speed-painting/create'

    data = {
        'fps': '30',        # Frames per second (30 or 60)
        'quality': 'hd',    # Can be 'sd' or 'hd'
        'sequence': 'auto', # How the drawing animation sequence is generated
        'hand_style': '1'   # Hand style (likely options: 0 = none, 1 = realistic, etc.)
    }

    files = [
        ('image', ('image.jpg', open('path/to/image.jpg', 'rb'), 'image/jpeg')),
        ('audio_file', ('audio.mp3', open('path/to/audio.mp3', 'rb'), 'audio/mpeg'))
    ]
    headers = {'X-API-Key': api_key}
    response = requests.request("POST", url, headers=headers, data=data, files=files)
    print(response.text)

if __name__ == '__main__':
    speedpainting()
```

{% endtab %}
{% endtabs %}

<mark style="color:green;">**Response**</mark>

```bash
{
    "success": true,
    "data": {
        "jobId":"0322d8f9-61de-4501-b49e-5ea4a2f297dc",
        "status":"PENDING",
        "originalImageUrl":"https://backend.miragic.ai/uploads/speedpainting/original/0f01f45d-37e5-4796-ae1d-4900-4722-97bd-93bfbfc03203.jpg"
        }, 
    "message":"Speed painting job created successfully"
}
```

**Response Field**

<table><thead><tr><th width="149">Field</th><th width="88">Type</th><th>Description</th></tr></thead><tbody><tr><td>jobId</td><td>String</td><td>A unique identifier used to track task status and retrieve results.</td></tr><tr><td>status</td><td>String</td><td>The initial status will be <code>PENDING</code>. Use the <code>Get Task Status API</code> to track progress.</td></tr><tr><td>originalImageUrl</td><td>String</td><td><code>URL</code> to input image</td></tr><tr><td>message</td><td>String</td><td>Explanation for result</td></tr><tr><td>success</td><td>Logic</td><td><code>true</code> or <code>false</code> to indicate whether task is successful or not.</td></tr></tbody></table>

## How To Get Task Status

<mark style="background-color:green;">GET</mark>   `/api/v1/speed-painting/jobs/:jobId`

This `API` lets you check the status of a try-on task and retrieve the final result. Because the try-on process runs asynchronously, you’ll need to poll this endpoint until the task is finished.

### Task Status:

<table><thead><tr><th width="107">Status</th><th>Description</th><th width="108">Progress</th><th>Next Action</th></tr></thead><tbody><tr><td><code>PENDING</code></td><td>Task is currently being processed.</td><td><code>0</code>~<code>99</code>%</td><td>Continue polling</td></tr><tr><td><code>COMPLETED</code></td><td>Task has finished successfully.</td><td><code>100</code>%</td><td>Download result using download_signed_url</td></tr><tr><td><code>FAILED</code></td><td>Task processing failed.</td><td>N/A</td><td>Check error details and retry if needed</td></tr></tbody></table>

{% hint style="info" %} <mark style="color:green;">**Progress Tracking:**</mark>

* The `progress` field indicates the percentage of task completion (`0`-`100`)
* Progress updates are available in real-time during the `PENDING` state
* Progress increases as the AI processes different stages of the try-on task
  {% endhint %}

{% hint style="info" %}
**Polling Guidelines:**

* Start polling immediately after creating the task
* Implement exponential backoff to avoid rate limiting
* The download\_signed\_url is temporary and should be used promptly
* Consider implementing a timeout after extended polling
  {% endhint %}

### Request:

<mark style="color:blue;">**URL Parameters**</mark>

<table><thead><tr><th width="118">Parameter</th><th width="80">Type</th><th width="109">Required</th><th>Description</th></tr></thead><tbody><tr><td>JobId</td><td>String</td><td>Yes</td><td>This value indicates task <code>ID</code> assigned by requesting <code>Speed-Painting</code> process <code>API</code></td></tr></tbody></table>

<mark style="color:blue;">**Request Example**</mark>

{% tabs %}
{% tab title="cURL" %}

```bash
curl -X GET https://backend.miragic.ai/api/v1/speed-painting/jobs/25331fb2-d0b0-44f6fcc85e3e \
  -H "X-API-Key: YOUR_API_KEY"
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const fetch = require("node-fetch"); // for Node.js

const apiKey = "YOUR_API_KEY";
const jobId = "25331fb2-d0b0-44f6fcc85e3e";
const url = `https://backend.miragic.ai/api/v1/speed-painting/jobs/${jobId}`;

const options = {
  method: "GET",
  headers: {
    "X-API-Key": apiKey
  }
};

fetch(url, options)
  .then(response => response.json())
  .then(data => {
    console.log("Response:", data);
  })
  .catch(error => {
    console.error("Error:", error);
  });
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

api_key = "YOUR_API_KEY"
job_id = "25331fb2-d0b0-44f6fcc85e3e"
url = f"https://backend.miragic.ai/api/v1/speed-painting/jobs/{job_id}"

headers = {
    "X-API-Key": api_key
}

response = requests.get(url, headers=headers)

# Print status code and response JSON
print("Status Code:", response.status_code)
print("Response:", response.json())
```

{% endtab %}
{% endtabs %}

<mark style="color:blue;">**Response Example**</mark>

#### Completed Status (200):

```bash
{
    'success': True, 
    'data': {
        'id': '941f9c37-aff6-4475-916b-50f4060a237e', 
        'userId': '0f01f45d-37e5-4796-ae1d-aca695a0d792', 
        'originalImagePath': '/var/www/html/miragicAI/backend/uploads/speedpainting/original/759849-a18f045c-fbf7-4693-a231-ce33707087b4.jpg', 
        'originalImageUrl': 'https://backend.miragic.ai/uploads/speedpainting/original/0f01f4545c-fbf7-4693-a231-ce33707087b4.jpg', 
        'processedVideoUrl': 'https://backend.miragic.ai/uploads/speedpainting/processed/0f01f45d-37e5-4796-ae1de34fc5c659.mp4', 
        'status': 'COMPLETED', 
        'errorMessage': None, 
        'metadata': {
            'externalVideoUrl': 'https://www.dropbox.com/scl/fi/lsflecx1xj7cxsbg4v2sw/aa96fe6e-ba91-469d-96a4-0e4143953f85.mp4?rlkey=lxy7zqoavvzgcwymr1w4qs0ln&raw=1', 
            'processingEndTime': '2025-10-10T18:42:54.983Z', 
            'processingDurationMs': 14899
            }, 
        'creditsUsed': 5, 
        'createdAt': '2025-10-10T18:42:40.008Z', 
        'updatedAt': '2025-10-10T18:42:54.984Z', 
        'processedUrl': 'https://backend.miragic.ai/uploads/speedpainting/processed/0f01f45d22d6d-4f2c-4d13-b11b-bce34fc5c659.mp4'
    }
}
```

<mark style="color:blue;">**Response Fields**</mark>

<table><thead><tr><th width="169">Field</th><th width="95">Type</th><th>Description</th></tr></thead><tbody><tr><td>id</td><td>String</td><td>Unique identifier of the task</td></tr><tr><td>status</td><td>String</td><td>Current status of the task (<code>PENDING</code>/<code>COMPLETED</code>/<code>FAILED</code>)</td></tr><tr><td>userId</td><td>String</td><td>Unique identifier of the user</td></tr><tr><td>originalImagePath</td><td>String</td><td>Path to input image</td></tr><tr><td>originalImageUrl</td><td>String</td><td><code>URL</code> to input image</td></tr><tr><td>externalVideoUrl</td><td>String</td><td><code>URL</code> to result video</td></tr><tr><td>resultImagePath</td><td>String</td><td><code>URL</code> to result image</td></tr><tr><td>createdAt</td><td>Number</td><td>Unix timestamp when processing is created</td></tr><tr><td>errorMessage</td><td>String</td><td>Error message</td></tr></tbody></table>

## Full Code Example

The following code lines are quick example to use our `API` in multiple languages.

<mark style="color:green;">**Case 1: Without audio input**</mark>

{% tabs %}
{% tab title="cURL" %}

```bash
curl -X POST "https://backend.miragic.ai/api/v1/speed-painting/create" \
  -H "X-API-Key: YOUR_API_KEY" \
  -F "fps=30" \
  -F "quality=hd" \
  -F "sequence=auto" \
  -F "hand_style=1" \
  -F "image=@path/to/image.jpg"
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
import axios from "axios";
import fs from "fs";
import FormData from "form-data";

const apiKey = "YOUR_API_KEY";
const baseUrl = "https://devapi.miragic.ai";

async function speedpainting() {
  const url = `${baseUrl}/api/v1/speed-painting/create`;

  // Form data with your settings
  const form = new FormData();
  form.append("fps", 60); // 30, 60
  form.append("quality", "hd"); // 'hd', 'sd'
  form.append("sequence", "vertical"); // 'auto', 'vertical'
  form.append("hand_style", 1); // 0, 1, 2, 3, 4
  form.append("image", fs.createReadStream("path/to/image.jpg"));

  const headers = {
    "X-API-Key": apiKey,
    ...form.getHeaders(),
  };

  try {
    // Step 1: Create job
    const response = await axios.post(url, form, { headers });
    console.log(response.data);

    if (response.data.success) {
      const jobId = response.data.data.jobId;
      console.log(`Job ID: ${jobId}`);

      // Step 2: Poll for results
      let status = "PENDING";
      while (status !== "COMPLETED" && status !== "FAILED") {
        await new Promise((r) => setTimeout(r, 2000)); // wait 2 sec
        const result = await axios.get(`${baseUrl}/api/v1/speed-painting/jobs/${jobId}`, {
          headers: { "X-API-Key": apiKey },
        });

        status = result.data.data.status;

        if (status === "COMPLETED") {
          console.log("Result:", result.data);
          break;
        } else if (status === "FAILED") {
          console.log("Job failed:", result.data);
          break;
        } else {
          console.log(`Current status: ${status}...`);
        }
      }
    } else {
      console.log("Error:", response.data);
    }
  } catch (error) {
    console.error("Request failed:", error.response?.data || error.message);
  }
}

speedpainting();
```

{% endtab %}

{% tab title="Python" %}

```python
import requests
import time

api_key = 'YOUR_API_KEY'   # ← Replace with your actual API key
base_url = 'https://backend.miragic.ai'

def speedpainting():
    url = f'{base_url}/api/v1/speed-painting/create'

    data = {
        'fps': '30',        # Frames per second (30 or 60)
        'quality': 'hd',    # Can be 'sd' or 'hd'
        'sequence': 'auto', # How the drawing animation sequence is generated
        'hand_style': '1'   # Hand style (likely options: 0 = none, 1 = realistic, etc.)
    }

    files = [
        ('image', ('image.jpg', open('path/to/image.jpg', 'rb'), 'image/jpeg'))
    ]
    headers = {'X-API-Key': api_key}
    response = requests.request("POST", url, headers=headers, data=data, files=files)
    print(response.text)
    
    if response.json()['success']:
        job_id = response.json()['data']['jobId']
        print(f'Job ID: {job_id}')
        
        # Poll for results
        while True:
            result = requests.get(
                f'{base_url}/api/v1/speed-painting/jobs/{job_id}',
                headers=headers
            )
            if result.json()['data']['status'] == 'COMPLETED':
                print('Result:', result.json())
                break
            elif result.json()['data']['status'] == 'FAILED':
                print('Job failed:', result.json())
                break
            time.sleep(2)
    else:
        print('Error:', response.json())

if __name__ == '__main__':
    speedpainting()
```

{% endtab %}
{% endtabs %}

<mark style="color:green;">**Case 2: With audio input**</mark>

{% tabs %}
{% tab title="cURL" %}

```bash
curl -X POST "https://backend.miragic.ai/api/v1/speed-painting/create" \
  -H "X-API-Key: YOUR_API_KEY" \
  -F "fps=30" \
  -F "quality=hd" \
  -F "sequence=auto" \
  -F "hand_style=1" \
  -F "image=@path/to/image.jpg"
  -F "audio_file=@path/to/audio.mp3"
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
import axios from "axios";
import fs from "fs";
import FormData from "form-data";

const apiKey = "YOUR_API_KEY";
const baseUrl = "https://devapi.miragic.ai";

async function speedpainting() {
  const url = `${baseUrl}/api/v1/speed-painting/create`;

  // Form data with your settings
  const form = new FormData();
  form.append("fps", 60); // 30, 60
  form.append("quality", "hd"); // 'hd', 'sd'
  form.append("sequence", "vertical"); // 'auto', 'vertical'
  form.append("hand_style", 1); // 0, 1, 2, 3, 4
  form.append("image", fs.createReadStream("path/to/image.jpg"));
  formData.append("audio_file", fs.createReadStream("path/to/audio.mp3"));

  const headers = {
    "X-API-Key": apiKey,
    ...form.getHeaders(),
  };

  try {
    // Step 1: Create job
    const response = await axios.post(url, form, { headers });
    console.log(response.data);

    if (response.data.success) {
      const jobId = response.data.data.jobId;
      console.log(`Job ID: ${jobId}`);

      // Step 2: Poll for results
      let status = "PENDING";
      while (status !== "COMPLETED" && status !== "FAILED") {
        await new Promise((r) => setTimeout(r, 2000)); // wait 2 sec
        const result = await axios.get(`${baseUrl}/api/v1/speed-painting/jobs/${jobId}`, {
          headers: { "X-API-Key": apiKey },
        });

        status = result.data.data.status;

        if (status === "COMPLETED") {
          console.log("Result:", result.data);
          break;
        } else if (status === "FAILED") {
          console.log("Job failed:", result.data);
          break;
        } else {
          console.log(`Current status: ${status}...`);
        }
      }
    } else {
      console.log("Error:", response.data);
    }
  } catch (error) {
    console.error("Request failed:", error.response?.data || error.message);
  }
}

speedpainting();
```

{% endtab %}

{% tab title="Python" %}

```python
import requests
import time

api_key = 'YOUR_API_KEY'   # ← Replace with your actual API key
base_url = 'https://backend.miragic.ai'

def speedpainting():
    url = f'{base_url}/api/v1/speed-painting/create'

    data = {
        'fps': '30',        # Frames per second (30 or 60)
        'quality': 'hd',    # Can be 'sd' or 'hd'
        'sequence': 'auto', # How the drawing animation sequence is generated
        'hand_style': '1'   # Hand style (likely options: 0 = none, 1 = realistic, etc.)
    }

    files = [
        ('image', ('image.jpg', open('path/to/image.jpg', 'rb'), 'image/jpeg')),
        ('audio_file', ('audio.mp3', open('path/to/audio.mp3', 'rb'), 'audio/mpeg'))
    ]
    headers = {'X-API-Key': api_key}
    response = requests.request("POST", url, headers=headers, data=data, files=files)
    print(response.text)
    if response.json()['success']:
        job_id = response.json()['data']['jobId']
        print(f'Job ID: {job_id}')
        # Poll for results
        while True:
            result = requests.get(
                f'{base_url}/api/v1/speed-painting/jobs/{job_id}',
                headers=headers
            )
            if result.json()['data']['status'] == 'COMPLETED':
                print('Result:', result.json())
                break
            elif result.json()['data']['status'] == 'FAILED':
                print('Job failed:', result.json())
                break
            time.sleep(2)
    else:
        print('Error:', response.json())

if __name__ == '__main__':
    speedpainting()
```

{% endtab %}
{% endtabs %}
