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

# Upload Magic Clips

> Generate short-form video clips by uploading a video file

# Upload Magic Clips

Generate multiple engaging short-form video clips by uploading a video file directly to Submagic. This endpoint accepts multipart/form-data uploads and creates clips with automatic captions, animations, and effects optimized for social media platforms.

<Note>
  This endpoint requires authentication, Magic Clips subscription, and has a
  rate limit of 500 requests per hour.
</Note>

<Note>
  Magic Clips created via API use your regular Magic Clips credits, not API
  credits. 1 credit per project.
</Note>

## Authentication

<ParamField header="x-api-key" type="string" required>
  Your Submagic API key starting with `sk-`
</ParamField>

## Request Body (multipart/form-data)

<ParamField body="file" type="file" required>
  Video file to upload. Must be in a supported format.
</ParamField>

<ParamField body="title" type="string" required>
  A descriptive title for your Magic Clips project (1-100 characters)
</ParamField>

<ParamField body="language" type="string" required>
  Language code for captions (e.g., "en", "es", "fr"). Use the [languages
  endpoint](/api-reference/languages) to get available options.
</ParamField>

<ParamField body="webhookUrl" type="string">
  URL to receive webhook notifications when processing is complete. Must be a
  valid HTTPS URL.
</ParamField>

<ParamField body="templateName" type="string">
  Template to apply for styling (max 50 characters). Use the [templates
  endpoint](/api-reference/templates) to get available options. Defaults to
  "Sara" if not specified. Cannot be used together with `userThemeId`.
</ParamField>

<ParamField body="userThemeId" type="string">
  ID of a custom user theme to apply for styling. Must be a valid UUID of a
  theme that belongs to you or your team. Cannot be used together with
  `templateName`.
</ParamField>

<ParamField body="minClipLength" type="number">
  Minimum clip duration in seconds (15-300 seconds). Defaults to 15 seconds.
</ParamField>

<ParamField body="maxClipLength" type="number">
  Maximum clip duration in seconds (15-300 seconds). Defaults to 60 seconds.
</ParamField>

<ParamField body="faceTracking" type="boolean">
  Enable face tracking for the generated clips. If not specified, defaults to `true`.
</ParamField>

## Supported Formats & Limits

<CardGroup cols={2}>
  <Card title="Supported Formats" icon="video">
    * **MP4** (.mp4) - **MOV** (.mov)
  </Card>

  <Card title="File Limits" icon="upload">
    * **Max size:** 10GB - **Max duration:** 120 minutes
  </Card>
</CardGroup>

## Response

<ResponseField name="id" type="string">
  Unique identifier for the created Magic Clips project (UUID format)
</ResponseField>

<ResponseField name="title" type="string">
  The title you provided for the project
</ResponseField>

<ResponseField name="language" type="string">
  Language code used for captions
</ResponseField>

<ResponseField name="status" type="string">
  Current processing status: `processing`
</ResponseField>

<ResponseField name="webhookUrl" type="string">
  Webhook URL if provided in the request
</ResponseField>

<ResponseField name="templateName" type="string">
  Template name applied to the project
</ResponseField>

<ResponseField name="userThemeId" type="string">
  User theme ID applied to the project
</ResponseField>

<ResponseField name="createdAt" type="string">
  ISO 8601 timestamp when the project was created
</ResponseField>

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST "https://api.submagic.co/v1/projects/magic-clips/upload" \
    -H "x-api-key: sk-your-api-key-here" \
    -F "file=@./video.mp4" \
    -F "title=My Magic Clips Upload" \
    -F "language=en" \
    -F "templateName=Hormozi 2" \
    -F "webhookUrl=https://yoursite.com/webhook/submagic" \
    -F "minClipLength=15" \
    -F "maxClipLength=60" \
    -F "faceTracking=true"
  ```

  ```javascript JavaScript theme={null}
  const uploadMagicClips = async (file) => {
    const formData = new FormData();
    formData.append("file", file);
    formData.append("title", "My Magic Clips Upload");
    formData.append("language", "en");
    formData.append("templateName", "Hormozi 2");
    formData.append("webhookUrl", "https://yoursite.com/webhook/submagic");
    formData.append("minClipLength", "15");
    formData.append("maxClipLength", "60");
    formData.append("faceTracking", "true");

    const response = await fetch(
      "https://api.submagic.co/v1/projects/magic-clips/upload",
      {
        method: "POST",
        headers: {
          "x-api-key": "sk-your-api-key-here",
        },
        body: formData,
      }
    );

    const project = await response.json();
    console.log("Magic Clips project created:", project.id);
    return project;
  };

  // Usage with file input
  const fileInput = document.getElementById("video-file");
  fileInput.addEventListener("change", async (event) => {
    const file = event.target.files[0];
    if (file) {
      await uploadMagicClips(file);
    }
  });
  ```

  ```python Python theme={null}
  import requests

  def upload_magic_clips(file_path):
      url = 'https://api.submagic.co/v1/projects/magic-clips/upload'
      headers = {
          'x-api-key': 'sk-your-api-key-here'
      }

      with open(file_path, 'rb') as video_file:
          files = {
              'file': video_file
          }
          data = {
              'title': 'My Magic Clips Upload',
              'language': 'en',
              'templateName': 'Hormozi 2',
              'webhookUrl': 'https://yoursite.com/webhook/submagic',
              'minClipLength': '15',
              'maxClipLength': '60',
              'faceTracking': 'true'
          }

          response = requests.post(url, headers=headers, files=files, data=data)
          project = response.json()

          print(f"Magic Clips project created: {project['id']}")
          return project

  # Usage
  project = upload_magic_clips('./video.mp4')
  ```
</RequestExample>

<ResponseExample>
  ```json 202 Accepted theme={null}
  {
    "id": "e568c322-7fa5-497a-8fb0-3ba32e9e67d2",
    "title": "My Magic Clips Upload",
    "language": "en",
    "status": "processing",
    "webhookUrl": "https://yoursite.com/webhook/submagic",
    "templateName": "Hormozi 2",
    "userThemeId": null,
    "createdAt": "2025-09-24T12:55:56.989Z"
  }
  ```
</ResponseExample>

## Webhook Integration

When processing is complete, a webhook notification will be sent to the provided `webhookUrl` (if specified). The webhook payload includes information about the main project and all generated clips.

### Webhook Payload Structure

```json theme={null}
{
  "projectId": "e568c322-7fa5-497a-8fb0-3ba32e9e67d2",
  "status": "completed",
  "title": "My Magic Clips Upload",
  "duration": 283,
  "completedAt": "2025-09-24T12:59:24.880Z",
  "magicClips": [
    {
      "id": "364fe092-b68d-468a-9558-bfca7c4d190e",
      "title": "ProMotion Display Breakthrough",
      "duration": 21.04,
      "viralityScores": {
        "total": 89,
        "shareability": 23,
        "hook_strength": 22,
        "story_quality": 21,
        "emotional_impact": 23
      },
      "status": "completed",
      "previewUrl": "https://app.submagic.co/view/364fe092-b68d-468a-9558-bfca7c4d190g",
      "downloadUrl": "https://app.submagic.co/api/file/download?path=3c6cd7cd-3f5e-4def-b662-69c48a7fc8ce/e568c322-7fa5-497a-8fb0-3ba32e9e67d2/364fe092-b68d-468a-9558-bfca7c4d130e-download.mp4&newFileName=ProMotion%20Display%20Breakthrough.mp4",
      "directUrl": "https://dqu1p08d61fh.cloudfront.net/api/9cc52d00-43d7-442f-9a22-050312bkm24f/1ddee5b4-101f-4c1e-a74a-b6b3bcfe206c/video.mp4-download.mp4"
    }
  ]
}
```

### Webhook Response Fields

<ResponseField name="projectId" type="string">
  Main project identifier
</ResponseField>

<ResponseField name="status" type="string">
  Processing status: `completed` or `failed`
</ResponseField>

<ResponseField name="title" type="string">
  Project title
</ResponseField>

<ResponseField name="duration" type="number">
  Total duration of the original video in seconds
</ResponseField>

<ResponseField name="completedAt" type="string">
  ISO 8601 timestamp when processing completed
</ResponseField>

<ResponseField name="magicClips" type="array">
  Array of generated clips, each containing: - `id`: Unique clip identifier -
  `title`: AI-generated clip title - `duration`: Clip duration in seconds -
  `viralityScores`: Object containing virality metrics with `total`, `shareability`, `hook_strength`, `story_quality`, and `emotional_impact` scores -
  `status`: Clip processing status - `previewUrl`: URL to preview the clip -
  `downloadUrl`: Direct download URL for the clip - `directUrl`: Direct URL that
  can be embedded on your website or used to play the clip directly
</ResponseField>

## Error Responses

<ResponseField name="400 Bad Request" type="object">
  ```json theme={null}
  {
    "error": "VALIDATION_ERROR",
    "message": "Content-Type must be multipart/form-data"
  }
  ```
</ResponseField>

<ResponseField name="400 Validation Error" type="object">
  ```json theme={null}
  {
    "error": "VALIDATION_ERROR",
    "message": "Invalid request parameters"
  }
  ```
</ResponseField>

<ResponseField name="401 Unauthorized" type="object">
  ```json theme={null}
  {
    "error": "UNAUTHORIZED",
    "message": "Invalid or missing API key"
  }
  ```
</ResponseField>

<ResponseField name="403 Forbidden" type="object">
  ```json theme={null}
  {
    "error": "FORBIDDEN",
    "message": "Magic Clips subscription required. Please upgrade your plan or purchase Magic Clips credits."
  }
  ```
</ResponseField>

<ResponseField name="413 Payload Too Large" type="object">
  ```json theme={null}
  {
    "error": "PAYLOAD_TOO_LARGE",
    "message": "File size exceeds maximum allowed size"
  }
  ```
</ResponseField>

<ResponseField name="415 Unsupported Media Type" type="object">
  ```json theme={null}
  {
    "error": "UNSUPPORTED_MEDIA_TYPE",
    "message": "Video format not supported"
  }
  ```
</ResponseField>

<ResponseField name="429 Too Many Requests" type="object">
  ```json theme={null}
  {
    "error": "RATE_LIMIT_EXCEEDED",
    "message": "Too many requests",
    "retryAfter": 60
  }
  ```
</ResponseField>

<ResponseField name="500 Internal Server Error" type="object">
  ```json theme={null}
  {
    "error": "INTERNAL_SERVER_ERROR",
    "message": "An unexpected error occurred",
    "details": {}
  }
  ```
</ResponseField>
