# Create Project
Source: https://docs.submagic.co/api-reference/create-project
POST https://api.submagic.co/v1/projects
Create a new video project using a video URL for AI-powered caption generation
# Create Project
Create a new video project by providing a video URL. This endpoint will download the video, transcribe the audio, and apply the specified template with AI-generated captions and effects.
This endpoint requires authentication and has a rate limit of 500 requests per
hour.
## Authentication
Your Submagic API key starting with `sk-`
## Request Body
A descriptive title for your video project (1-100 characters)
Language code for transcription (e.g., "en", "es", "fr"). Use the [languages
endpoint](/api-reference/languages) to get available options.
Public URL to your video file. Must be accessible without authentication and
in a supported format.
Name of an AI edit template to apply. AI edit templates automatically apply
AI-powered scene splitting, B-roll, music, and styling to your video. Available templates: `"kelly"` (minimal, design), `"karl"`
(effective, modern), `"ella"` (dynamic, bold).
When `aiEditTemplate` is provided, the only other fields you can pass
alongside it are `title`, `language`, `videoUrl`, `webhookUrl`, and
`dictionary`. All other fields will be ignored or rejected.
ID of a saved preset to apply to the project. A preset is a snapshot of your
project settings — including captions style, hook title, music, effects, and
more. When provided, all preset settings are automatically applied after
transcription completes. To get your preset ID, go to the Presets page in the
app, open the dropdown menu on any preset card, and click **"Copy ID"**.
Cannot be combined with `templateName`, `userThemeId`, `hookTitle`, `music`,
`items`, `magicZooms`, `magicBrolls`, `magicBrollsPercentage`,
`removeSilencePace`, or `removeBadTakes` — the preset already controls these
settings.
Template to apply for styling. Use the [templates
endpoint](/api-reference/templates) to get available options. Defaults to
"Sara" if not specified. Cannot be used together with `userThemeId`.
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`. You can find the id of your custom theme by opening a project,
selecting the theme, pressing the pen icon to edit it. You'll see the id of
the theme under theme's name.
Adds an animated opening caption (hook) to the video. Set to `true` to let AI
generate using the default style or pass an object to customize the
appearance:
* `text` (optional): Custom hook, 1-100 characters
* `template` (optional): Hook title template name. Defaults to `"tiktok"`.
Use the [hook title templates
endpoint](/api-reference/hook-title-templates) to retrieve valid options.
* `top` (optional): Vertical position (0-80). Defaults to `50`.
* `size` (optional): Font size (0-80). Defaults to `30`.
URL to receive webhook notifications when processing is complete. Must be a
valid HTTPS URL.
Array of custom words or phrases to improve transcription accuracy (max 100
items, 50 characters each).
Optional array of items to insert into the video. Each item must include
a `type` field to specify whether it's user media from your library or
AI-generated content.
Must be set to `"user-media"`
Start time in seconds where the media should begin (must be ≥ 0)
End time in seconds where the media should end (must be greater than
`startTime`)
UUID of the user media from your library. You can find this ID in the
editor's 'B-roll' tab → 'My videos' section under each video.
Layout mode for the inserted media. Available values depend on the media type:
**Video media:** `"cover"`, `"contain"`, `"rounded"`, `"square"`, `"split-50-50"`, `"split-35-65"`, `"split-50-50-bordered"`, `"split-35-65-bordered"`, `"pip-top-right"`, `"pip-bottom-right"`
**Image media:** `"cover"`, `"contain"`, `"rounded"`, `"square"`
Must be set to `"ai-broll"`
Start timestamp in seconds (must be ≥ 0)
End timestamp in seconds. Must be greater than `startTime` and no more than
12 seconds later.
Describes what the AI B-roll should depict (1-2500 characters)
Layout mode for the AI B-roll. Allowed values: `"cover"`, `"contain"`, `"rounded"`, `"square"`, `"split-50-50"`, `"split-35-65"`, `"split-50-50-bordered"`, `"split-35-65-bordered"`, `"pip-top-right"`, `"pip-bottom-right"`
**Important:** Each item must have a `type` field. Items cannot overlap with
each other in time. Requests that include invalid durations, overlapping
ranges, or prompts outside the allowed length will be rejected.
Enable automatic zoom effects on the video to enhance visual engagement.
Optional, defaults to false.
Enable automatic B-roll insertion to enhance video content with relevant
supplementary footage. Optional, defaults to false.
Percentage of automatic B-rolls to include in the video (0-100). Only
effective when magicBrolls is enabled. Optional, defaults to 50.
Automatically remove silence from the video at the specified pace. Optional.
Allowed values: `natural`, `fast`, `extra-fast`. - `extra-fast`: 0.1-0.2
seconds of silence removal - `fast`: 0.2-0.6 seconds of silence removal -
`natural`: 0.6+ seconds of silence removal
Automatically detect and remove bad takes and silence from the video using AI
analysis. Optional, defaults to false.
Enable AI-powered audio cleanup that removes background noises from the video.
Optional, defaults to false.
Hide captions from the exported video. Optional, defaults to `false`.
Optional background music track that spans the full project duration. The
referenced media must be an `AUDIO` type file in your user media library.
UUID of an audio file from your media library. Must reference a user media
item with type `AUDIO`. Use the [List User Media](/api-reference/list-user-media)
endpoint to find available audio files.
Playback volume (1-100)
Start offset within the audio file in seconds. Defaults to `0`.
Apply a fade-in/fade-out effect to the music track. Defaults to `true`.
## Supported Formats & Limits
* **MP4** (.mp4) - **MOV** (.mov)
* **Max size:** 2GB - **Max duration:** 2 hours
## Response
Unique identifier for the created project (UUID format)
The title you provided for the project
Language code used for transcription
Current processing status: `processing`, `transcribing`, `exporting`,
`completed`, or `failed`
Webhook URL if provided in the request
Template name applied to the project
User theme ID applied to the project
Preset ID applied to the project
AI edit template applied to the project (if any): `kelly`, `karl`, or `ella`
Whether automatic zoom effects are enabled for the video
Whether automatic B-roll insertion is enabled for the video
Percentage of automatic B-rolls to include in the video (0-100)
Pace setting for automatic silence removal: `natural`, `fast`, or `extra-fast`
Whether automatic bad takes and silence removal is enabled
Whether AI-powered audio cleanup is enabled
ISO 8601 timestamp when the project was created
ISO 8601 timestamp when the project was last updated
```bash cURL theme={null}
curl -X POST "https://api.submagic.co/v1/projects" \
-H "x-api-key: sk-your-api-key-here" \
-H "Content-Type: application/json" \
-d '{
"title": "My Awesome Video",
"language": "en",
"videoUrl": "https://example.com/videos/sample.mp4",
"templateName": "Hormozi 2",
"items": [
{
"type": "user-media",
"startTime": 5,
"endTime": 10,
"userMediaId": "123e4567-e89b-12d3-a456-426614174000",
"layout": "pip-top-right"
},
{
"type": "ai-broll",
"startTime": 15,
"endTime": 21,
"prompt": "person performing multiple back exercises on gym equipment",
"layout": "split-50-50"
}
],
"webhookUrl": "https://yoursite.com/webhook/submagic",
"dictionary": ["Submagic", "AI-powered", "captions"],
"magicZooms": true,
"magicBrolls": true,
"magicBrollsPercentage": 75,
"removeSilencePace": "fast",
"removeBadTakes": true,
"cleanAudio": true,
"hookTitle": {
"text": "Stop scrolling—watch this in 30 seconds",
"template": "tiktok",
"top": 45,
"size": 32
},
"music": {
"userMediaId": "88a08eec-712a-45d0-8d0b-3b631700cb3a",
"volume": 30,
"startFromTime": 0,
"fade": true
}
}'
```
```javascript JavaScript theme={null}
const createProject = async () => {
const response = await fetch("https://api.submagic.co/v1/projects", {
method: "POST",
headers: {
"x-api-key": "sk-your-api-key-here",
"Content-Type": "application/json",
},
body: JSON.stringify({
title: "My Awesome Video",
language: "en",
videoUrl: "https://example.com/videos/sample.mp4",
templateName: "Hormozi 2",
items: [
{
type: "user-media",
startTime: 5,
endTime: 10,
userMediaId: "123e4567-e89b-12d3-a456-426614174000",
layout: "pip-top-right",
},
{
type: "ai-broll",
startTime: 15,
endTime: 21,
prompt:
"person performing multiple back exercises on gym equipment",
layout: "split-50-50",
},
],
webhookUrl: "https://yoursite.com/webhook/submagic",
dictionary: ["Submagic", "AI-powered", "captions"],
magicZooms: true,
magicBrolls: true,
magicBrollsPercentage: 75,
removeSilencePace: "fast",
removeBadTakes: true,
cleanAudio: true,
hookTitle: {
text: "Stop scrolling—watch this in 30 seconds",
template: "tiktok",
top: 45,
size: 32,
},
music: {
userMediaId: "88a08eec-712a-45d0-8d0b-3b631700cb3a",
volume: 30,
startFromTime: 0,
fade: true,
},
}),
});
const project = await response.json();
console.log("Project created:", project.id);
return project;
};
```
```python Python theme={null}
import requests
import json
def create_project():
url = 'https://api.submagic.co/v1/projects'
headers = {
'x-api-key': 'sk-your-api-key-here',
'Content-Type': 'application/json'
}
data = {
'title': 'My Awesome Video',
'language': 'en',
'videoUrl': 'https://example.com/videos/sample.mp4',
'templateName': 'Hormozi 2',
'items': [
{
'type': 'user-media',
'startTime': 5,
'endTime': 10,
'userMediaId': '123e4567-e89b-12d3-a456-426614174000',
'layout': 'pip-top-right'
},
{
'type': 'ai-broll',
'startTime': 15,
'endTime': 21,
'prompt': 'person performing multiple back exercises on gym equipment',
'layout': 'split-50-50'
}
],
'webhookUrl': 'https://yoursite.com/webhook/submagic',
'dictionary': ['Submagic', 'AI-powered', 'captions'],
'magicZooms': True,
'magicBrolls': True,
'magicBrollsPercentage': 75,
'removeSilencePace': 'fast',
'removeBadTakes': True,
'cleanAudio': True,
'hookTitle': {
'text': 'Stop scrolling—watch this in 30 seconds',
'template': 'tiktok',
'top': 45,
'size': 32
},
'music': {
'userMediaId': '88a08eec-712a-45d0-8d0b-3b631700cb3a',
'volume': 30,
'startFromTime': 0,
'fade': True
}
}
response = requests.post(url, headers=headers, json=data)
project = response.json()
print(f"Project created: {project['id']}")
return project
```
```json 201 Created theme={null}
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"title": "My Awesome Video",
"language": "en",
"status": "processing",
"webhookUrl": "https://yoursite.com/webhook/submagic",
"templateName": "Hormozi 2",
"magicZooms": true,
"magicBrolls": true,
"magicBrollsPercentage": 75,
"removeSilencePace": "fast",
"removeBadTakes": true,
"cleanAudio": true,
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:30:00.000Z"
}
```
## Custom Dictionary
Improve transcription accuracy by providing custom terms:
```json theme={null}
{
"dictionary": [
"Submagic",
"API endpoint",
"captions",
"transcription",
"AI-powered",
"webhook notification"
]
}
```
**Best practices for dictionary terms:**
* Include brand names, product names, or technical terms
* Add words that are frequently mispronounced or misunderstood
* Keep terms under 50 characters each
* Limit to 100 terms per project
## Webhook Integration
Receive notifications when your project is complete:
```json theme={null}
{
"webhookUrl": "https://yoursite.com/webhook/submagic"
}
```
Your webhook endpoint will receive a POST request:
```json theme={null}
{
"projectId": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"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",
"timestamp": "2024-01-15T10:45:00.000Z"
}
```
## Error Responses
```json theme={null}
{
"error": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": [
{
"field": "videoUrl",
"message": "Must be a valid URL",
"value": "invalid-url"
}
]
}
```
```json theme={null}
{
"error": "VALIDATION_ERROR",
"message": "presetId cannot be combined with templateName, userThemeId, magicZooms, magicBrolls, magicBrollsPercentage, removeBadTakes, removeSilencePace, items, hookTitle, or music. The preset controls these settings."
}
```
```json theme={null}
{
"error": "VALIDATION_ERROR",
"message": "Invalid template name"
}
```
```json theme={null}
{
"error": "VALIDATION_ERROR",
"message": "URLs from social media platforms are not supported. Please use a direct download link."
}
```
```json theme={null}
{
"error": "UNAUTHORIZED",
"message": "Invalid or missing API key"
}
```
```json theme={null}
{
"error": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests",
"retryAfter": 30
}
```
```json theme={null}
{
"error": "INTERNAL_SERVER_ERROR",
"message": "An unexpected error occurred"
}
```
# Export Project
Source: https://docs.submagic.co/api-reference/export-project
POST https://api.submagic.co/v1/projects/{id}/export
Trigger the rendering/export process for a completed project to generate the final video
# Export Project
Triggers the rendering/export process for a completed project. This starts the video generation process asynchronously with customizable output parameters.
This endpoint requires authentication and has enhanced rate limits for
API-generated projects. The export process is asynchronous - use webhooks or
polling to track completion.
## Authentication
Your Submagic API key starting with `sk-`
## Path Parameters
The unique identifier (UUID) of the project to export
## Request Body (Optional)
All parameters are optional. If not provided, the system uses optimal defaults based on the project's original video metadata.
Frames per second for the exported video (1-60). Defaults to project's
original fps or 30.
Video width in pixels (100-4000). Defaults to project's original width or
1080\.
Video height in pixels (100-4000). Defaults to project's original height or
1920\.
URL to receive notification when export is complete. Must be a valid URL
format.
## Prerequisites
Before exporting a project, ensure:
* **Project is transcribed**: Must have words data available
* **Project is not uploading**: Cannot be in "uploading" status
* **Project ownership**: Must belong to the authenticated user
* **API-generated project**: Must be created via API
## Response
Success message confirming the export has started
The unique identifier of the project being exported
Current status of the project after export trigger
## Error Responses
Error code: `NOT_FOUND`, `BAD_REQUEST`, or `INTERNAL_SERVER_ERROR`
Detailed error message explaining the issue
## Webhook Notifications
If you provide a `webhookUrl`, the system will send a POST request to your URL when export completes,
including export details and download URL in the notification.
## Export Status Tracking
After triggering an export:
1. **Monitor Progress**: Call `GET /v1/projects/{id}` to check export progress
2. **Check Download URL**: The `downloadUrl` and `directUrl` fields will be populated once rendering is complete
```bash cURL - Basic Export theme={null}
curl -X POST "https://api.submagic.co/v1/projects/550e8400-e29b-41d4-a716-446655440000/export" \
-H "x-api-key: sk-your-api-key-here" \
-H "Content-Type: application/json"
```
```bash cURL - Custom Parameters theme={null}
curl -X POST "https://api.submagic.co/v1/projects/550e8400-e29b-41d4-a716-446655440000/export" \
-H "x-api-key: sk-your-api-key-here" \
-H "Content-Type: application/json" \
-d '{
"fps": 30,
"width": 1920,
"height": 1080,
"webhookUrl": "https://yoursite.com/webhook/export-complete"
}'
```
```javascript JavaScript - Basic Export theme={null}
const exportProject = async (projectId) => {
const response = await fetch(
`https://api.submagic.co/v1/projects/${projectId}/export`,
{
method: "POST",
headers: {
"x-api-key": "sk-your-api-key-here",
"Content-Type": "application/json",
},
}
);
const result = await response.json();
console.log("Export started:", result.message);
return result;
};
// Usage
const result = await exportProject("550e8400-e29b-41d4-a716-446655440000");
```
```javascript JavaScript - With Custom Parameters theme={null}
const exportProjectWithOptions = async (projectId, options = {}) => {
const response = await fetch(
`https://api.submagic.co/v1/projects/${projectId}/export`,
{
method: "POST",
headers: {
"x-api-key": "sk-your-api-key-here",
"Content-Type": "application/json",
},
body: JSON.stringify(options),
}
);
const result = await response.json();
console.log("Export started:", result.message);
return result;
};
// Usage with options
const exportOptions = {
fps: 30,
width: 1920,
height: 1080,
webhookUrl: "https://yoursite.com/webhook/export-complete",
};
const result = await exportProjectWithOptions(
"550e8400-e29b-41d4-a716-446655440000",
exportOptions
);
```
```python Python - Basic Export theme={null}
import requests
def export_project(project_id):
url = f'https://api.submagic.co/v1/projects/{project_id}/export'
headers = {
'x-api-key': 'sk-your-api-key-here',
'Content-Type': 'application/json'
}
response = requests.post(url, headers=headers)
result = response.json()
print(f"Export started: {result.get('message')}")
return result
# Usage
result = export_project('550e8400-e29b-41d4-a716-446655440000')
```
```python Python - With Custom Parameters theme={null}
import requests
import json
def export_project_with_options(project_id, options=None):
url = f'https://api.submagic.co/v1/projects/{project_id}/export'
headers = {
'x-api-key': 'sk-your-api-key-here',
'Content-Type': 'application/json'
}
response = requests.post(url, headers=headers, json=options)
result = response.json()
print(f"Export started: {result.get('message')}")
return result
# Usage with options
export_options = {
'fps': 30,
'width': 1920,
'height': 1080,
'webhookUrl': 'https://yoursite.com/webhook/export-complete'
}
result = export_project_with_options(
'550e8400-e29b-41d4-a716-446655440000',
export_options
)
```
```json 200 OK - Export Started theme={null}
{
"message": "Export started successfully",
"projectId": "550e8400-e29b-41d4-a716-446655440000",
"status": "exporting"
}
```
```json 404 NOT_FOUND - Project Not Found theme={null}
{
"error": "NOT_FOUND",
"message": "Project doesn't exist or doesn't belong to user"
}
```
```json 400 BAD_REQUEST - Project Not Ready theme={null}
{
"error": "BAD_REQUEST",
"message": "Project not ready for export"
}
```
```json 400 BAD_REQUEST - Invalid Parameters theme={null}
{
"error": "BAD_REQUEST",
"message": "Invalid fps value. Must be between 1 and 60"
}
```
```json 500 INTERNAL_SERVER_ERROR - Export Failed theme={null}
{
"error": "INTERNAL_SERVER_ERROR",
"message": "Export failed to start"
}
```
**Tip**: After triggering an export, use the [Get
Project](/api-reference/get-project) endpoint to monitor the export progress.
The `downloadUrl` and `directUrl` fields will be populated once the rendering
is complete.
**Important**: The export process is asynchronous. The API will return
immediately after starting the export, but the actual video rendering happens
in the background. Use webhooks or polling to track completion status.
# Get Project
Source: https://docs.submagic.co/api-reference/get-project
GET https://api.submagic.co/v1/projects/{id}
Retrieve details of a specific video project including processing status and download links
# Get Project
Retrieve detailed information about a specific video project, including its current processing status, metadata, and download links when available.
This endpoint requires authentication and has a rate limit of 100 requests per
hour.
## Authentication
Your Submagic API key starting with `sk-`
## Path Parameters
The unique identifier (UUID) of the project to retrieve
## Response
Unique identifier of the project (UUID format)
The title of the project
AI description of the project
Language code used for transcription
Current processing status: `processing`, `transcribing`, `exporting`,
`completed`, or `failed`
Webhook URL if provided in the request
Template name applied to the project (if using a built-in template)
User theme ID applied to the project (if using a custom theme)
Preset ID applied to the project (if using a preset)
AI edit template applied to the project (if any): `kelly`, `karl`, or `ella`
Direct download URL for the processed video (available when status is
`completed`)
Direct URL that can be embedded on your website or used to play the video
directly (available when status is `completed`)
User-friendly preview page URL where the exported project can be previewed or
downloaded: `https://app.submagic.co/view/{projectId}` (available when status
is `completed`)
Current transcription status: `PROCESSING`, `COMPLETED`, or `FAILED`
Reason for failure if status is `failed`
Whether automatic zoom effects are enabled for the video
Whether automatic B-roll insertion is enabled for the video
Percentage of automatic B-rolls to include in the video (0-100)
Pace setting for automatic silence removal: `natural`, `fast`, or `extra-fast`
Whether automatic bad takes and silence removal is enabled
Video metadata extracted from the source
Video width in pixels
Video height in pixels
Video duration in seconds
Frames per second (optional)
ISO 8601 timestamp when the project was created
ISO 8601 timestamp when the project was last updated
Array of transcribed words and silence segments with timing information (available when transcription is completed)
Unique identifier for the word or silence segment
The transcribed text content (empty string for silence segments)
Type of segment: `word` for spoken words, `silence` for silent periods, or `punctuation` for punctuation marks
Start time of the word/silence in seconds
End time of the word/silence in seconds
Array of generated Magic Clips (only present for Magic Clips projects)
Unique identifier for the Magic Clip (UUID format)
AI-generated title for the clip
Duration of the clip in seconds
Object containing virality metrics for the clip (available when status is `completed`)
Overall virality score (0-100)
Shareability score (0-100)
Hook strength score (0-100)
Story quality score (0-100)
Emotional impact score (0-100)
Processing status of the clip: `processing`, `completed`, or `failed`
URL to preview the Magic Clip (available when status is `completed`)
Direct download URL for the Magic Clip (available when status is `completed`)
Direct URL that can be embedded on your website or used to play the Magic Clip directly (available when status is `completed`)
```bash cURL theme={null}
curl -X GET "https://api.submagic.co/v1/projects/550e8400-e29b-41d4-a716-446655440000" \
-H "x-api-key: sk-your-api-key-here"
```
```javascript JavaScript theme={null}
const getProject = async (projectId) => {
const response = await fetch(
`https://api.submagic.co/v1/projects/${projectId}`,
{
method: "GET",
headers: {
"x-api-key": "sk-your-api-key-here",
},
}
);
const project = await response.json();
console.log("Project status:", project.status);
return project;
};
// Usage
const project = await getProject("550e8400-e29b-41d4-a716-446655440000");
```
```python Python theme={null}
import requests
def get_project(project_id):
url = f'https://api.submagic.co/v1/projects/{project_id}'
headers = {
'x-api-key': 'sk-your-api-key-here'
}
response = requests.get(url, headers=headers)
project = response.json()
print(f"Project status: {project['status']}")
return project
# Usage
project = get_project('550e8400-e29b-41d4-a716-446655440000')
```
```json 200 OK - Transcribing theme={null}
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"title": "My Awesome Video",
"description": "AI description of the project",
"language": "en",
"status": "transcribing",
"webhookUrl": "https://yoursite.com/webhook/submagic",
"templateName": "Hormozi 2",
"transcriptionStatus": "PROCESSING",
"magicZooms": true,
"magicBrolls": true,
"magicBrollsPercentage": 75,
"removeSilencePace": "fast",
"removeBadTakes": true,
"videoMetaData": {
"width": 1920,
"height": 1080,
"duration": 185.2,
"fps": 30
},
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:32:15.000Z"
}
```
```json 200 OK - Processing with Transcription Complete theme={null}
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"title": "My Awesome Video",
"description": "AI description of the project",
"language": "en",
"status": "processing",
"webhookUrl": "https://yoursite.com/webhook/submagic",
"templateName": "Hormozi 2",
"transcriptionStatus": "COMPLETED",
"magicZooms": true,
"magicBrolls": true,
"magicBrollsPercentage": 75,
"removeSilencePace": "fast",
"removeBadTakes": true,
"videoMetaData": {
"width": 1920,
"height": 1080,
"duration": 185.2,
"fps": 30
},
"words": [
{
"id": "silence_f9164d70-ac39-41cc-ae31-5b9de3093a2f",
"text": "",
"type": "silence",
"startTime": 0,
"endTime": 0.08
},
{
"id": "w1EydrN5",
"text": "Do",
"type": "word",
"startTime": 0.08,
"endTime": 0.16
},
{
"id": "8VUdYfGN",
"text": "you",
"type": "word",
"startTime": 0.16,
"endTime": 0.24
},
{
"id": "eZBH24RB",
"text": "mind",
"type": "word",
"startTime": 0.24,
"endTime": 0.32
},
{
"id": "RFLElgda",
"text": "if",
"type": "word",
"startTime": 0.32,
"endTime": 0.4
},
{
"id": "iTWC5pc9",
"text": "we",
"type": "word",
"startTime": 0.4,
"endTime": 0.52
},
{
"id": "EpT23tSM",
"text": "do",
"type": "word",
"startTime": 0.52,
"endTime": 0.68
},
{
"id": "KtVZ8VDN",
"text": "something",
"type": "word",
"startTime": 0.68,
"endTime": 0.84
},
{
"id": "LtVZ8CM",
"text": "?",
"type": "punctuation",
"startTime": 0.84,
"endTime": 0.84
}
],
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:35:20.000Z"
}
```
```json 200 OK - Completed with Custom Theme theme={null}
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"title": "My Awesome Video",
"description": "AI description of the project",
"language": "en",
"status": "completed",
"webhookUrl": "https://yoursite.com/webhook/submagic",
"userThemeId": "730e9500-f29c-52e5-b826-556755550001",
"downloadUrl": "https://api.submagic.co/download/550e8400-e29b-41d4-a716-446655440000",
"directUrl": "https://dqu1p08d61fh.cloudfront.net/api/9cc52d00-43d7-442f-9a22-050312bkm24f/1ddee5b4-101f-4c1e-a74a-b6b3bcfe206c/video.mp4-download.mp4",
"previewUrl": "https://app.submagic.co/view/550e8400-e29b-41d4-a716-446655440000",
"transcriptionStatus": "COMPLETED",
"magicZooms": false,
"magicBrolls": true,
"magicBrollsPercentage": 30,
"removeSilencePace": "natural",
"removeBadTakes": false,
"videoMetaData": {
"width": 1920,
"height": 1080,
"duration": 185.2,
"fps": 30
},
"words": [
{
"id": "silence_f9164d70-ac39-41cc-ae31-5b9de3093a2f",
"text": "",
"type": "silence",
"startTime": 0,
"endTime": 0.08
},
{
"id": "w1EydrN5",
"text": "Do",
"type": "word",
"startTime": 0.08,
"endTime": 0.16
},
{
"id": "8VUdYfGN",
"text": "you",
"type": "word",
"startTime": 0.16,
"endTime": 0.24
},
{
"id": "eZBH24RB",
"text": "mind",
"type": "word",
"startTime": 0.24,
"endTime": 0.32
},
{
"id": "RFLElgda",
"text": "if",
"type": "word",
"startTime": 0.32,
"endTime": 0.4
},
{
"id": "iTWC5pc9",
"text": "we",
"type": "word",
"startTime": 0.4,
"endTime": 0.52
},
{
"id": "EpT23tSM",
"text": "do",
"type": "word",
"startTime": 0.52,
"endTime": 0.68
},
{
"id": "KtVZ8VDN",
"text": "something",
"type": "word",
"startTime": 0.68,
"endTime": 0.84
},
{
"id": "LtVZ8CM",
"text": "?",
"type": "punctuation",
"startTime": 0.84,
"endTime": 0.84
}
],
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:45:30.000Z"
}
```
```json 200 OK - Failed theme={null}
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"title": "My Awesome Video",
"description": "AI description of the project",
"language": "en",
"status": "failed",
"webhookUrl": "https://yoursite.com/webhook/submagic",
"templateName": "Hormozi 2",
"transcriptionStatus": "FAILED",
"failureReason": "Video format not supported or file corrupted",
"magicZooms": true,
"magicBrolls": false,
"magicBrollsPercentage": 50,
"removeSilencePace": null,
"removeBadTakes": false,
"videoMetaData": {
"width": 1920,
"height": 1080,
"duration": 185.2,
"fps": 30
},
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:33:45.000Z"
}
```
```json 200 OK - Magic Clips Project Completed theme={null}
{
"id": "e568c322-7fa5-497a-8fb0-3ba32e9e67d2",
"title": "YT magic-clips test1",
"description": "AI description of the project",
"language": "en",
"status": "completed",
"webhookUrl": "https://webhook-test.com/6704c8535a2a913c3d094aba685d6a19",
"previewUrl": "https://app.submagic.co/view/e568c322-7fa5-497a-8fb0-3ba32e9e67d3",
"transcriptionStatus": "COMPLETED",
"videoMetaData": {
"width": 1920,
"height": 1080,
"duration": 283,
"fps": 30
},
"magicClips": [
{
"id": "5c26f199-08dd-4fab-911f-5378c23cc103",
"title": "Fusion Camera Technology",
"duration": 24.8,
"viralityScores": {
"total": 87,
"shareability": 22,
"hook_strength": 21,
"story_quality": 22,
"emotional_impact": 22
},
"status": "completed",
"previewUrl": "https://app.submagic.co/view/5c26f199-08dd-4fab-911f-5378c23cc104",
"downloadUrl": "https://api.submagic.co/api/file/download?path=3c6cd7cd-3f5e-4def-b662-69c48a7fc8ce/e568c322-7fa5-497a-8fb0-3ba32e9e67d2/5c26f199-08dd-4fac-911f-5378c23cc103-download.mp4&newFileName=Fusion%20Camera%20Technology.mp4",
"directUrl": "https://dqu1p08d61fh.cloudfront.net/api/9cc52d00-43d7-442f-9a22-050312bkm24f/1ddee5b4-101f-4c1e-a74a-b6b3bcfe206c/video.mp4-download.mp4"
},
{
"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://api.submagic.co/api/file/download?path=3c6cd7cd-3f5e-4def-b662-69c48a7fc8ce/e568c322-7fa5-497a-8fb0-3ba32e9e67d2/364fe092-b68d-468a-9558-bfca7c4d189c-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"
}
],
"createdAt": "2025-09-24T12:55:56.989Z",
"updatedAt": "2025-09-24T12:59:24.880Z"
}
```
# Health Check
Source: https://docs.submagic.co/api-reference/health
GET https://api.submagic.co/health
Check the health status of the Submagic API
# Health Check
The health endpoint provides a simple way to check if the Submagic API is operational. This endpoint does not require authentication and can be used for monitoring and health checks.
This endpoint does not require authentication and is not subject to rate
limiting.
## Request
This endpoint does not accept any parameters.
## Response
The current status of the API service
The name of the service
ISO 8601 timestamp of when the response was generated
```bash cURL theme={null}
curl -X GET "https://api.submagic.co/health"
```
```javascript JavaScript theme={null}
const response = await fetch("https://api.submagic.co/health");
const data = await response.json();
console.log(data);
```
```python Python theme={null}
import requests
response = requests.get('https://api.submagic.co/health')
data = response.json()
print(data)
```
```json Response theme={null}
{
"status": "healthy",
"service": "submagic-public-api",
"timestamp": "2024-01-15T10:30:00.000Z"
}
```
## Use Cases
Use this endpoint to monitor API availability in your monitoring systems
Configure your load balancer to use this endpoint for health checks
Verify API availability before running integration tests
Include this endpoint in your status page monitoring
## Status Values
| Status | Description |
| ----------- | ----------------------------------------------- |
| `healthy` | API is operational and ready to accept requests |
| `degraded` | API is operational but experiencing issues |
| `unhealthy` | API is not operational |
## Example Monitoring Script
Here's a simple monitoring script you can use:
```bash Bash theme={null}
#!/bin/bash
response=$(curl -s -o /dev/null -w "%{http_code}" https://api.submagic.co/health)
if [ $response -eq 200 ]; then
echo "API is healthy"
exit 0
else
echo "API is not responding (HTTP $response)"
exit 1
fi
```
```javascript Node.js theme={null}
const checkHealth = async () => {
try {
const response = await fetch("https://api.submagic.co/health");
const data = await response.json();
if (response.ok && data.status === "healthy") {
console.log("✅ API is healthy");
return true;
} else {
console.log("❌ API is not healthy:", data);
return false;
}
} catch (error) {
console.log("❌ Failed to check API health:", error.message);
return false;
}
};
// Run health check every 30 seconds
setInterval(checkHealth, 30000);
```
```python Python theme={null}
import requests
import time
import sys
def check_health():
try:
response = requests.get('https://api.submagic.co/health', timeout=10)
data = response.json()
if response.status_code == 200 and data.get('status') == 'healthy':
print('✅ API is healthy')
return True
else:
print(f'❌ API is not healthy: {data}')
return False
except Exception as e:
print(f'❌ Failed to check API health: {e}')
return False
if __name__ == '__main__':
is_healthy = check_health()
sys.exit(0 if is_healthy else 1)
```
The health endpoint is designed to respond quickly (typically under 100ms) to
provide fast health check results for monitoring systems.
# Get Hook Title Templates
Source: https://docs.submagic.co/api-reference/hook-title-templates
GET https://api.submagic.co/v1/hook-title/templates
Retrieve a list of all available hook title templates
# Get Hook Title Templates
Retrieve a list of all hookt title available templates that can be applied to your projects. Hook titles render an animated caption for the first seconds of your video to grab attention before the main captions begin.
This endpoint requires authentication and has a rate limit of 1000 requests
per hour.
## Authentication
Your Submagic API key starting with `sk-`
## Response
Array of available hook title template names
```bash cURL theme={null}
curl -X GET "https://api.submagic.co/v1/hook-title/templates" \
-H "x-api-key: sk-your-api-key-here"
```
```javascript JavaScript theme={null}
const response = await fetch(
"https://api.submagic.co/v1/hook-title/templates",
{
headers: { "x-api-key": "sk-your-api-key-here" },
}
);
const data = await response.json();
console.log(data.templates);
```
```python Python theme={null}
import requests
headers = {
'x-api-key': 'sk-your-api-key-here'
}
response = requests.get(
'https://api.submagic.co/v1/hook-title/templates',
headers=headers
)
data = response.json()
print("Available templates:", data['templates'])
```
```json Response theme={null}
{
"templates": [
"tiktok",
"laura",
"steph",
"kevin",
"kelly",
"mark",
"logan",
"enrico",
"mike",
"devin",
"hormozi",
"masi",
"ali"
]
}
```
## Using Hook Title Templates
When creating a project or updating a project, specify the template name in your request:
```json Example theme={null}
"hookTitle": {
"text": "Stop scrolling—watch this in 30 seconds",
"template": "tiktok",
"top": 45,
"size": 32
}
```
## Response
Array of available hook title template names
## Request Example
```bash cURL theme={null}
curl -X GET "https://api.submagic.co/v1/hook-title/templates" \
-H "x-api-key: sk-your-api-key-here"
```
```javascript JavaScript theme={null}
const response = await fetch(
"https://api.submagic.co/v1/hook-title/templates",
{
headers: { "x-api-key": "sk-your-api-key-here" },
}
);
const { templates } = await response.json();
console.log("Hook title templates:", templates);
```
```python Python theme={null}
import requests
headers = {'x-api-key': 'sk-your-api-key-here'}
response = requests.get(
'https://api.submagic.co/v1/hook-title/templates',
headers=headers
)
templates = response.json()['templates']
print("Hook title templates:", templates)
```
## Example Response
```json theme={null}
{
"templates": [
"tiktok",
"laura",
"steph",
"kevin",
"kelly",
"mark",
"logan",
"enrico",
"mike",
"devin",
"hormozi",
"masi",
"ali"
]
}
```
## Error Handling
```json theme={null}
{
"error": "UNAUTHORIZED",
"message": "Invalid or missing API key"
}
```
```json theme={null}
{
"error": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests",
"retryAfter": 30
}
```
```json theme={null}
{
"error": "VALIDATION_ERROR",
"message": "Invalid template name"
}
```
If you don't specify a `hookTitle` when creating a project, the system will
automatically apply the "tiktok" template.
Template names are case-sensitive. Make sure to use the exact template name as
returned by this endpoint.
# Get Languages
Source: https://docs.submagic.co/api-reference/languages
GET https://api.submagic.co/v1/languages
Retrieve a list of all supported languages for video transcription
# Get Languages
Retrieve a comprehensive list of all supported languages for video transcription and caption generation. This endpoint returns language codes and names that can be used when creating projects.
This endpoint requires authentication and has a rate limit of 100 requests per
minute.
## Authentication
Your Submagic API key starting with `sk-`
## Response
Array of supported language objects
Human-readable name of the language (e.g., "English", "Spanish")
ISO language code used for API requests (e.g., "en", "es", "fr")
```bash cURL theme={null}
curl -X GET "https://api.submagic.co/v1/languages" \
-H "x-api-key: sk-your-api-key-here"
```
```javascript JavaScript theme={null}
const response = await fetch("https://api.submagic.co/v1/languages", {
headers: {
"x-api-key": "sk-your-api-key-here",
},
});
const data = await response.json();
console.log(data.languages);
```
```python Python theme={null}
import requests
headers = {
'x-api-key': 'sk-your-api-key-here'
}
response = requests.get('https://api.submagic.co/v1/languages', headers=headers)
data = response.json()
for language in data['languages']:
print(f"{language['name']}: {language['code']}")
```
```json Response theme={null}
{
"languages": [
{
"name": "English",
"code": "en"
},
{
"name": "Spanish",
"code": "es"
},
{
"name": "French",
"code": "fr"
},
{
"name": "German",
"code": "de"
},
{
"name": "Italian",
"code": "it"
},
{
"name": "Portuguese",
"code": "pt"
},
{
"name": "Dutch",
"code": "nl"
},
{
"name": "Russian",
"code": "ru"
},
{
"name": "Chinese (Simplified)",
"code": "zh"
},
{
"name": "Japanese",
"code": "ja"
},
{
"name": "Korean",
"code": "ko"
},
{
"name": "Arabic",
"code": "ar"
},
{
"name": "Hindi",
"code": "hi"
}
]
}
```
## Popular Languages
Here are some of the most commonly used language codes:
**Code:** `en` Most widely supported with highest accuracy
**Code:** `es` Excellent support for Latin American and European Spanish
**Code:** `fr` High accuracy for both European and Canadian French
**Code:** `de` Great support for German language nuances
**Code:** `it` Optimized for Italian pronunciation patterns
**Code:** `pt` Supports both Brazilian and European Portuguese
## Using Language Codes
Once you have the language codes, you can use them when creating projects:
```json theme={null}
{
"title": "My Spanish Video",
"language": "es",
"videoUrl": "https://example.com/spanish-video.mp4"
}
```
## Best Practices
### Caching Language Data
Since the list of supported languages doesn't change frequently, consider caching the response:
```javascript theme={null}
class LanguageCache {
constructor() {
this.cache = null;
this.lastFetch = null;
this.cacheDuration = 24 * 60 * 60 * 1000; // 24 hours
}
async getLanguages() {
const now = Date.now();
if (
this.cache &&
this.lastFetch &&
now - this.lastFetch < this.cacheDuration
) {
return this.cache;
}
const response = await fetch("https://api.submagic.co/v1/languages", {
headers: { "x-api-key": process.env.SUBMAGIC_API_KEY },
});
this.cache = await response.json();
this.lastFetch = now;
return this.cache;
}
}
```
### Building Language Selectors
Create user-friendly language selection interfaces:
```javascript theme={null}
const buildLanguageSelector = (languages) => {
return languages
.sort((a, b) => a.name.localeCompare(b.name))
.map((lang) => ({
value: lang.code,
label: lang.name,
flag: getFlagEmoji(lang.code), // Helper function for flag emojis
}));
};
```
## Error Responses
```json theme={null}
{
"error": "UNAUTHORIZED",
"message": "Invalid or missing API key"
}
```
```json theme={null}
{
"error": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests",
"retryAfter": 30
}
```
```json theme={null}
{
"error": "INTERNAL_SERVER_ERROR",
"message": "An unexpected error occurred"
}
```
The language list is updated periodically as we add support for new languages.
We recommend fetching this list dynamically rather than hard-coding language
options in your application.
# List Published Projects
Source: https://docs.submagic.co/api-reference/list-published-projects
GET https://api.submagic.co/v1/projects/published
List your published posts with optional platform filtering and cursor-based pagination, including per-platform status and analytics
# List Published Projects
Retrieve a paginated list of your published posts across YouTube, TikTok, and
Instagram. Each entry includes per-platform delivery status, post URLs, and
analytics where available.
This endpoint requires authentication.
## Authentication
Your Submagic API key starting with `sk-`
## Query Parameters
Filter by platform. Allowed values: `youtube`, `tiktok`, `instagram`.
Number of items to return per page (1-100). Defaults to `25`.
Cursor for pagination. Pass the `nextCursor` value from a previous response
to fetch the next page of results.
## Response
Array of publication objects
Unique identifier of the publication
The unique identifier of the underlying project
ISO 8601 timestamp of when the publication was published or is scheduled
to publish
`true` if the publication was scheduled for a future time
Overall publication status: `published`, `scheduled`, `processing`, or
`failed`
Minimal project metadata
Project UUID
Project title
Array of per-platform post objects
Platform identifier: `youtube`, `tiktok`, or `instagram`
The post ID assigned by the upstream platform
Public URL of the post on the platform
Per-platform delivery status: `published`, `scheduled`, `processing`,
or `failed`
Analytics sync status: `synced`, `pending`, or `failed`
Error message if the post failed to publish, otherwise `null`
Platform analytics (available once `syncStatus` is `synced`)
Total views
Total likes
Total comments
Total shares
Total impressions
Unique accounts reached
Total link clicks
Engagement rate as a decimal (e.g. `0.125` = 12.5%)
Cursor-based pagination metadata
Cursor to pass as the `cursor` query parameter to fetch the next page.
`null` or omitted when there are no more results.
```bash cURL theme={null}
curl -X GET "https://api.submagic.co/v1/projects/published?platform=youtube&limit=25" \
-H "x-api-key: sk-your-api-key-here"
```
```javascript JavaScript theme={null}
const listPublishedProjects = async ({ platform, limit, cursor } = {}) => {
const params = new URLSearchParams();
if (platform) params.set("platform", platform);
if (limit) params.set("limit", String(limit));
if (cursor) params.set("cursor", cursor);
const response = await fetch(
`https://api.submagic.co/v1/projects/published?${params}`,
{
headers: {
"x-api-key": "sk-your-api-key-here",
},
}
);
const data = await response.json();
console.log(`Fetched ${data.data.length} publications`);
return data;
};
// Fetch first page of YouTube publications
const page = await listPublishedProjects({ platform: "youtube", limit: 25 });
// Fetch next page
if (page.pagination.nextCursor) {
const nextPage = await listPublishedProjects({
platform: "youtube",
limit: 25,
cursor: page.pagination.nextCursor,
});
}
```
```python Python theme={null}
import requests
def list_published_projects(platform=None, limit=None, cursor=None):
url = 'https://api.submagic.co/v1/projects/published'
headers = {
'x-api-key': 'sk-your-api-key-here'
}
params = {}
if platform:
params['platform'] = platform
if limit:
params['limit'] = limit
if cursor:
params['cursor'] = cursor
response = requests.get(url, headers=headers, params=params)
data = response.json()
print(f"Fetched {len(data['data'])} publications")
return data
# Fetch first page of YouTube publications
page = list_published_projects(platform='youtube', limit=25)
# Fetch next page
next_cursor = page.get('pagination', {}).get('nextCursor')
if next_cursor:
next_page = list_published_projects(
platform='youtube',
limit=25,
cursor=next_cursor
)
```
```json 200 OK theme={null}
{
"data": [
{
"id": "pub_8f3b4c2e-1a9d-4e5f-9b7c-2a4d6e8f1c3b",
"projectId": "550e8400-e29b-41d4-a716-446655440000",
"publishedAt": "2026-04-28T12:34:56.000Z",
"scheduled": false,
"status": "published",
"project": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"title": "How I Doubled My Revenue in 30 Days"
},
"posts": [
{
"platform": "youtube",
"platformPostId": "dQw4w9WgXcQ",
"platformPostUrl": "https://youtube.com/watch?v=dQw4w9WgXcQ",
"publishStatus": "published",
"syncStatus": "synced",
"error": null,
"analytics": {
"views": 1234,
"likes": 56,
"comments": 7,
"shares": 2,
"impressions": 5000,
"reach": 800,
"clicks": 120,
"engagementRate": 0.125
}
}
]
}
],
"pagination": {
"nextCursor": "eyJpZCI6InB1Yl83YjJjM2Q0ZSJ9"
}
}
```
```json 200 OK (empty) theme={null}
{
"data": [],
"pagination": {
"nextCursor": null
}
}
```
## Error Responses
```json theme={null}
{
"error": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": [
{
"field": "platform",
"message": "Must be one of: youtube, tiktok, instagram",
"value": "facebook"
}
]
}
```
```json theme={null}
{
"error": "UNAUTHORIZED",
"message": "Invalid or missing API key"
}
```
```json theme={null}
{
"error": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests",
"retryAfter": 30
}
```
```json theme={null}
{
"error": "INTERNAL_SERVER_ERROR",
"message": "An unexpected error occurred"
}
```
# List User Media
Source: https://docs.submagic.co/api-reference/list-user-media
GET https://api.submagic.co/v1/user-media
List the media files in your library
# List User Media
Retrieve a paginated list of media files from your library. You can filter by media type and control pagination using cursor-based navigation.
This endpoint requires authentication and has a rate limit of 500 requests per
hour.
## Authentication
Your Submagic API key starting with `sk-`
## Query Parameters
Filter by media type. Allowed values: `VIDEO`, `AUDIO`, `IMAGE`.
Number of items to return per page (1-100). Defaults to `50`.
UUID cursor for pagination. Pass the `nextCursor` value from a previous
response to fetch the next page of results.
## Response
Array of user media objects
Unique identifier for the media file (UUID format)
Media type: `VIDEO`, `AUDIO`, or `IMAGE`
Original file name of the uploaded media
URL to access the media file
File metadata that varies by media type
Width in pixels (VIDEO and IMAGE only)
Height in pixels (VIDEO and IMAGE only)
Duration in seconds (VIDEO and AUDIO only)
File size in bytes
ISO 8601 timestamp when the media was uploaded
Whether there are more items available beyond this page
Cursor to pass as the `cursor` query parameter to fetch the next page.
Only present when `hasMore` is `true`.
```bash cURL theme={null}
curl -X GET "https://api.submagic.co/v1/user-media?type=AUDIO&limit=10" \
-H "x-api-key: sk-your-api-key-here"
```
```javascript JavaScript theme={null}
const listUserMedia = async ({ type, limit, cursor } = {}) => {
const params = new URLSearchParams();
if (type) params.set("type", type);
if (limit) params.set("limit", String(limit));
if (cursor) params.set("cursor", cursor);
const response = await fetch(
`https://api.submagic.co/v1/user-media?${params}`,
{
headers: {
"x-api-key": "sk-your-api-key-here",
},
}
);
const data = await response.json();
console.log(`Fetched ${data.items.length} items`);
return data;
};
// Fetch first page of audio files
const page = await listUserMedia({ type: "AUDIO", limit: 10 });
// Fetch next page
if (page.hasMore) {
const nextPage = await listUserMedia({
type: "AUDIO",
limit: 10,
cursor: page.nextCursor,
});
}
```
```python Python theme={null}
import requests
def list_user_media(media_type=None, limit=None, cursor=None):
url = 'https://api.submagic.co/v1/user-media'
headers = {
'x-api-key': 'sk-your-api-key-here'
}
params = {}
if media_type:
params['type'] = media_type
if limit:
params['limit'] = limit
if cursor:
params['cursor'] = cursor
response = requests.get(url, headers=headers, params=params)
data = response.json()
print(f"Fetched {len(data['items'])} items")
return data
# Fetch first page of audio files
page = list_user_media(media_type='AUDIO', limit=10)
# Fetch next page
if page['hasMore']:
next_page = list_user_media(
media_type='AUDIO',
limit=10,
cursor=page['nextCursor']
)
```
```json 200 OK theme={null}
{
"items": [
{
"id": "88a08eec-712a-45d0-8d0b-3b631700cb3a",
"type": "AUDIO",
"fileName": "background-music.mp3",
"url": "https://cdn.submagic.co/media/88a08eec-712a-45d0-8d0b-3b631700cb3a.mp3",
"metadata": {
"duration": 180.5,
"fileSize": 4500000
},
"createdAt": "2024-01-10T08:00:00.000Z"
},
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"type": "VIDEO",
"fileName": "broll-clip.mp4",
"url": "https://cdn.submagic.co/media/a1b2c3d4-e5f6-7890-abcd-ef1234567890.mp4",
"metadata": {
"width": 1920,
"height": 1080,
"duration": 12.3,
"fileSize": 15000000
},
"createdAt": "2024-01-09T14:30:00.000Z"
}
],
"hasMore": true,
"nextCursor": "f5e4d3c2-b1a0-9876-fedc-ba0987654321"
}
```
```json 200 OK (empty) theme={null}
{
"items": [],
"hasMore": false
}
```
## Error Responses
```json theme={null}
{
"error": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": [
{
"field": "type",
"message": "Must be one of: VIDEO, AUDIO, IMAGE",
"value": "INVALID"
}
]
}
```
```json theme={null}
{
"error": "UNAUTHORIZED",
"message": "Invalid or missing API key"
}
```
```json theme={null}
{
"error": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests",
"retryAfter": 30
}
```
```json theme={null}
{
"error": "INTERNAL_SERVER_ERROR",
"message": "An unexpected error occurred"
}
```
# Create Magic Clips
Source: https://docs.submagic.co/api-reference/magic-clips
POST https://api.submagic.co/v1/projects/magic-clips
Automatically generate short-form video clips from YouTube videos
# Create Magic Clips
Generate multiple engaging short-form video clips from YouTube videos automatically. This endpoint processes YouTube videos and creates clips with automatic captions, animations, and effects optimized for social media platforms.
This endpoint requires authentication, Magic Clips subscription, and has a
rate limit of 500 requests per hour.
Magic Clips created via API use your regular Magic Clips credits, not API
credits. 1 credit per project.
## Authentication
Your Submagic API key starting with `sk-`
## Request Body
A descriptive title for your Magic Clips project (1-100 characters)
Language code for captions (2-10 characters, e.g., "en", "es", "fr"). Use the
[languages endpoint](/api-reference/languages) to get available options.
Valid YouTube video URL to process for clip generation
URL to receive webhook notifications when processing is complete. Must be a
valid HTTPS URL.
Template to apply for styling. Use the [templates
endpoint](/api-reference/templates) to get available options. Defaults to
"Sara" if not specified. Cannot be used together with `userThemeId`.
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`.
Minimum clip duration in seconds (15-300 seconds). Defaults to 15 seconds.
Maximum clip duration in seconds (15-300 seconds). Defaults to 60 seconds.
Enable face tracking for the generated clips. If not specified, defaults to `true`.
## Response
Unique identifier for the created Magic Clips project (UUID format)
The title you provided for the project
Language code used for captions
Current processing status: `processing`
Webhook URL if provided in the request
Template name applied to the project
User theme ID applied to the project
ISO 8601 timestamp when the project was created
```bash cURL theme={null}
curl -X POST "https://api.submagic.co/v1/projects/magic-clips" \
-H "x-api-key: sk-your-api-key-here" \
-H "Content-Type: application/json" \
-d '{
"title": "YT magic-clips test1",
"language": "en",
"youtubeUrl": "https://www.youtube.com/watch?v=eURYVajKLVw",
"templateName": "Hormozi 2",
"webhookUrl": "https://webhook-test.com/6704c8535a2a913c3d094aba685d6a18",
"minClipLength": 15,
"maxClipLength": 60,
"faceTracking": true
}'
```
```javascript JavaScript theme={null}
const createMagicClips = async () => {
const response = await fetch(
"https://api.submagic.co/v1/projects/magic-clips",
{
method: "POST",
headers: {
"x-api-key": "sk-your-api-key-here",
"Content-Type": "application/json",
},
body: JSON.stringify({
title: "YT magic-clips test1",
language: "en",
youtubeUrl: "https://www.youtube.com/watch?v=eURYVajKLVw",
templateName: "Hormozi 2",
webhookUrl: "https://webhook-test.com/6704c8535a2a913c3d094aba685d6a18",
minClipLength: 15,
maxClipLength: 60,
faceTracking: true,
}),
}
);
const project = await response.json();
console.log("Magic Clips project created:", project.id);
return project;
};
```
```python Python theme={null}
import requests
import json
def create_magic_clips():
url = 'https://api.submagic.co/v1/projects/magic-clips'
headers = {
'x-api-key': 'sk-your-api-key-here',
'Content-Type': 'application/json'
}
data = {
'title': 'YT magic-clips test1',
'language': 'en',
'youtubeUrl': 'https://www.youtube.com/watch?v=eURYVajKLVw',
'templateName': 'Hormozi 2',
'webhookUrl': 'https://webhook-test.com/6704c8535a2a913c3d094aba685d6a18',
'minClipLength': 15,
'maxClipLength': 60,
'faceTracking': True
}
response = requests.post(url, headers=headers, json=data)
project = response.json()
print(f"Magic Clips project created: {project['id']}")
return project
```
```json 202 Accepted theme={null}
{
"id": "e568c322-7fa5-497a-8fb0-3ba32e9e67d2",
"title": "YT magic-clips test1",
"language": "en",
"status": "processing",
"webhookUrl": "https://webhook-test.com/6704c8535a2a913c3d094aba685d6a18",
"templateName": "Hormozi 2",
"userThemeId": "749a19fs-4b45-4b14-bd31-a3970a1a5ff2",
"createdAt": "2025-09-24T12:55:56.989Z"
}
```
## 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": "YT magic-clips test1",
"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
Main project identifier
Processing status: `completed` or `failed`
Project title
Total duration of the original video in seconds
ISO 8601 timestamp when processing completed
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
## Error Responses
```json theme={null}
{
"error": "VALIDATION_ERROR",
"message": "Invalid request parameters"
}
```
```json theme={null}
{
"error": "UNAUTHORIZED",
"message": "Invalid or missing API key"
}
```
```json theme={null}
{
"error": "FORBIDDEN",
"message": "Magic Clips subscription required. Please upgrade your plan or purchase Magic Clips credits."
}
```
```json theme={null}
{
"error": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests",
"retryAfter": 60
}
```
```json theme={null}
{
"error": "INTERNAL_SERVER_ERROR",
"message": "An unexpected error occurred",
"details": {}
}
```
# Upload Magic Clips
Source: https://docs.submagic.co/api-reference/magic-clips-upload
POST https://api.submagic.co/v1/projects/magic-clips/upload
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.
This endpoint requires authentication, Magic Clips subscription, and has a
rate limit of 500 requests per hour.
Magic Clips created via API use your regular Magic Clips credits, not API
credits. 1 credit per project.
## Authentication
Your Submagic API key starting with `sk-`
## Request Body (multipart/form-data)
Video file to upload. Must be in a supported format.
A descriptive title for your Magic Clips project (1-100 characters)
Language code for captions (e.g., "en", "es", "fr"). Use the [languages
endpoint](/api-reference/languages) to get available options.
URL to receive webhook notifications when processing is complete. Must be a
valid HTTPS URL.
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`.
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`.
Minimum clip duration in seconds (15-300 seconds). Defaults to 15 seconds.
Maximum clip duration in seconds (15-300 seconds). Defaults to 60 seconds.
Enable face tracking for the generated clips. If not specified, defaults to `true`.
## Supported Formats & Limits
* **MP4** (.mp4) - **MOV** (.mov)
* **Max size:** 10GB - **Max duration:** 120 minutes
## Response
Unique identifier for the created Magic Clips project (UUID format)
The title you provided for the project
Language code used for captions
Current processing status: `processing`
Webhook URL if provided in the request
Template name applied to the project
User theme ID applied to the project
ISO 8601 timestamp when the project was created
```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')
```
```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"
}
```
## 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
Main project identifier
Processing status: `completed` or `failed`
Project title
Total duration of the original video in seconds
ISO 8601 timestamp when processing completed
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
## Error Responses
```json theme={null}
{
"error": "VALIDATION_ERROR",
"message": "Content-Type must be multipart/form-data"
}
```
```json theme={null}
{
"error": "VALIDATION_ERROR",
"message": "Invalid request parameters"
}
```
```json theme={null}
{
"error": "UNAUTHORIZED",
"message": "Invalid or missing API key"
}
```
```json theme={null}
{
"error": "FORBIDDEN",
"message": "Magic Clips subscription required. Please upgrade your plan or purchase Magic Clips credits."
}
```
```json theme={null}
{
"error": "PAYLOAD_TOO_LARGE",
"message": "File size exceeds maximum allowed size"
}
```
```json theme={null}
{
"error": "UNSUPPORTED_MEDIA_TYPE",
"message": "Video format not supported"
}
```
```json theme={null}
{
"error": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests",
"retryAfter": 60
}
```
```json theme={null}
{
"error": "INTERNAL_SERVER_ERROR",
"message": "An unexpected error occurred",
"details": {}
}
```
# Publish Project
Source: https://docs.submagic.co/api-reference/publish-project
POST https://api.submagic.co/v1/projects/{id}/publish
Publish or schedule an exported project to one or more social platforms (YouTube, TikTok, Instagram) in a single call
# Publish Project
Publish or schedule an exported project to one or more connected social platforms (YouTube, TikTok, Instagram).
This endpoint requires authentication. The project must already be exported
(have a `downloadUrl`), and the social accounts you want to publish to must be
connected from the [Publishing](https://app.submagic.co/publishing) page in
the dashboard.
## Authentication
Your Submagic API key starting with `sk-`
## Path Parameters
The unique identifier (UUID) of the exported project to publish
## Request Body
ISO 8601 timestamp to schedule the publication for a future time. Omit to
publish immediately.
Per-platform publishing options. At least one platform must be provided.
YouTube publishing options
Video title (max 100 characters)
Video description
Array of tag strings
Pinned first comment to post under the video
TikTok publishing options
Caption / post content
Instagram publishing options
Publishing format: `reel` or `story`
Caption text. Required when `format` is `reel`.
Pinned first comment to post under the reel
## Prerequisites
Before publishing a project, ensure:
* **Project is exported**: The project must have a `downloadUrl` (status `completed`)
* **Project ownership**: The project must belong to the authenticated user
* **Accounts connected**: Each requested platform must be connected from the [Publishing](https://app.submagic.co/publishing) page in the dashboard
* **Sufficient credits**: Your account must have enough API credits
## Response
Unique identifier of the publication
The unique identifier of the project being published
`true` if the publication was scheduled for a future time, `false` if
published immediately
ISO 8601 timestamp of when the publication was published or is scheduled to
publish
Current status of the publication: `published`, `scheduled`, `processing`, or
`failed`
Array of platforms the project is being published to
```bash cURL - Publish Now theme={null}
curl -X POST "https://api.submagic.co/v1/projects/550e8400-e29b-41d4-a716-446655440000/publish" \
-H "x-api-key: sk-your-api-key-here" \
-H "Content-Type: application/json" \
-d '{
"platforms": {
"youtube": {
"title": "How I Doubled My Revenue in 30 Days",
"description": "In this video I break down the exact playbook I used.",
"tags": ["business", "marketing", "growth"],
"firstComment": "Drop a comment if this was helpful!"
},
"tiktok": {
"content": "The exact playbook I used to double my revenue 🚀 #business #marketing"
}
}
}'
```
```bash cURL - Schedule for Later theme={null}
curl -X POST "https://api.submagic.co/v1/projects/550e8400-e29b-41d4-a716-446655440000/publish" \
-H "x-api-key: sk-your-api-key-here" \
-H "Content-Type: application/json" \
-d '{
"scheduledFor": "2026-05-01T15:00:00Z",
"platforms": {
"instagram": {
"format": "reel",
"content": "New video out now ✨",
"firstComment": "Tap the link in bio for the full episode!"
}
}
}'
```
```javascript JavaScript theme={null}
const publishProject = async (projectId, payload) => {
const response = await fetch(
`https://api.submagic.co/v1/projects/${projectId}/publish`,
{
method: "POST",
headers: {
"x-api-key": "sk-your-api-key-here",
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
}
);
const result = await response.json();
console.log("Publication:", result.id, result.status);
return result;
};
// Publish to YouTube and TikTok now
const result = await publishProject("550e8400-e29b-41d4-a716-446655440000", {
platforms: {
youtube: {
title: "How I Doubled My Revenue in 30 Days",
description: "In this video I break down the exact playbook I used.",
tags: ["business", "marketing", "growth"],
},
tiktok: {
content: "The exact playbook I used to double my revenue 🚀",
},
},
});
```
```python Python theme={null}
import requests
def publish_project(project_id, payload):
url = f'https://api.submagic.co/v1/projects/{project_id}/publish'
headers = {
'x-api-key': 'sk-your-api-key-here',
'Content-Type': 'application/json'
}
response = requests.post(url, headers=headers, json=payload)
result = response.json()
print(f"Publication: {result.get('id')} - {result.get('status')}")
return result
# Schedule a reel on Instagram
payload = {
'scheduledFor': '2026-05-01T15:00:00Z',
'platforms': {
'instagram': {
'format': 'reel',
'content': 'New video out now ✨',
'firstComment': 'Tap the link in bio for the full episode!'
}
}
}
result = publish_project(
'550e8400-e29b-41d4-a716-446655440000',
payload
)
```
```json 200 OK - Published Now theme={null}
{
"id": "pub_8f3b4c2e-1a9d-4e5f-9b7c-2a4d6e8f1c3b",
"projectId": "550e8400-e29b-41d4-a716-446655440000",
"scheduled": false,
"publishedAt": "2026-04-28T12:34:56.000Z",
"status": "published",
"platforms": ["youtube", "tiktok"]
}
```
```json 202 Accepted - Scheduled theme={null}
{
"id": "pub_8f3b4c2e-1a9d-4e5f-9b7c-2a4d6e8f1c3b",
"projectId": "550e8400-e29b-41d4-a716-446655440000",
"scheduled": true,
"publishedAt": "2026-05-01T15:00:00.000Z",
"status": "scheduled",
"platforms": ["instagram"]
}
```
```json 400 BAD_REQUEST - Project Not Exported theme={null}
{
"error": "BAD_REQUEST",
"message": "Project must be exported before it can be published"
}
```
```json 402 PAYMENT_REQUIRED - Insufficient Credits theme={null}
{
"error": "PAYMENT_REQUIRED",
"message": "Insufficient API credits to publish"
}
```
```json 403 FORBIDDEN - Plan Restriction theme={null}
{
"error": "FORBIDDEN",
"message": "Your plan does not allow publishing via the API"
}
```
```json 404 NOT_FOUND - Project Not Found theme={null}
{
"error": "NOT_FOUND",
"message": "Project doesn't exist or doesn't belong to user"
}
```
```json 412 PRECONDITION_FAILED - Account Not Connected theme={null}
{
"error": "PRECONDITION_FAILED",
"message": "Some platform accounts are not connected"
}
```
## Tracking Published Posts
After publishing, use the [List Published Projects](/api-reference/list-published-projects)
endpoint to retrieve the status, platform URLs, and analytics of your posts.
**Tip**: To connect a YouTube, TikTok, or Instagram account, visit the
[Publishing](https://app.submagic.co/publishing) page in the dashboard. Once
connected, the account is available to all publish requests for that user.
**Important**: Publication to upstream platforms is asynchronous. A `200`
response means the request was accepted and posts have been dispatched, but
individual platform delivery (especially for scheduled posts) is finalized in
the background. Poll the [List Published Projects](/api-reference/list-published-projects)
endpoint to track per-platform `publishStatus`.
# Get Templates
Source: https://docs.submagic.co/api-reference/templates
GET https://api.submagic.co/v1/templates
Retrieve a list of all available video templates for styling and effects
# Get Templates
Retrieve a list of all available video templates that can be applied to your projects. Templates define the visual styling, animations, and effects that will be applied to your captions and video content.
This endpoint requires authentication and has a rate limit of 1000 requests per
hour.
## Authentication
Your Submagic API key starting with `sk-`
## Response
Array of available template names that can be used in project creation
```bash cURL theme={null}
curl -X GET "https://api.submagic.co/v1/templates" \
-H "x-api-key: sk-your-api-key-here"
```
```javascript JavaScript theme={null}
const response = await fetch("https://api.submagic.co/v1/templates", {
headers: {
"x-api-key": "sk-your-api-key-here",
},
});
const data = await response.json();
console.log(data.templates);
```
```python Python theme={null}
import requests
headers = {
'x-api-key': 'sk-your-api-key-here'
}
response = requests.get('https://api.submagic.co/v1/templates', headers=headers)
data = response.json()
print("Available templates:", data['templates'])
```
```json Response theme={null}
{
"templates": [
"Matt",
"Jess",
"Jack",
"Nick",
"Laura",
"Kelly 2",
"Caleb",
"Kendrick",
"Lewis",
"Doug",
"Carlos",
"Luke",
"Leila",
"Mark",
"Sara",
"Daniel",
"Dan 2",
"Hormozi 4",
"Dan",
"Devin",
"Tayo",
"Ella",
"Tracy",
"Hormozi 1",
"Hormozi 2",
"Hormozi 3",
"Hormozi 5",
"Jason",
"William",
"Leon",
"Ali",
"Beast",
"Maya",
"Karl",
"Iman",
"Umi",
"David",
"Noah",
"Gstaad",
"Malta",
"Nema",
"seth"
]
}
```
## Using Templates
When creating a project, specify the template name in your request:
```json URL-based Project theme={null}
{
"title": "My Trendy Video",
"language": "en",
"videoUrl": "https://example.com/video.mp4",
"templateName": "Hormozi 2"
}
```
```json File Upload Project theme={null}
{
"title": "Professional Presentation",
"language": "en",
"templateName": "Hormozi 2",
"file": "video.mp4"
}
```
## Template Features
Each template includes:
* **Caption Styling**: Font family, size, color, and positioning
* **Animation Effects**: How captions appear and disappear
* **Visual Elements**: Background shapes, highlights, and decorative elements
* **Color Schemes**: Coordinated color palettes optimized for the template theme
* **Emoji Integration**: How emojis are displayed and animated
* **Layout Options**: Caption positioning and text alignment
## Template Preview
While the API doesn't provide template previews directly, you can create small
test projects with different templates to see how they look with your content
style.
### Template Testing Strategy
```javascript theme={null}
const testTemplates = async (videoUrl, templates) => {
const results = [];
for (const template of templates.slice(0, 3)) {
// Test first 3 templates
try {
const response = await fetch("https://api.submagic.co/v1/projects", {
method: "POST",
headers: {
"x-api-key": process.env.SUBMAGIC_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
title: `Test - ${template}`,
language: "en",
videoUrl: videoUrl,
templateName: template,
}),
});
const project = await response.json();
results.push({ template, projectId: project.id });
// Wait between requests to respect rate limits
await new Promise((resolve) => setTimeout(resolve, 2000));
} catch (error) {
console.error(`Failed to test template ${template}:`, error);
}
}
return results;
};
```
## Best Practices
### Caching Templates
Since templates don't change frequently, cache the list:
```javascript theme={null}
class TemplateCache {
constructor() {
this.templates = null;
this.lastFetch = null;
this.cacheFor = 6 * 60 * 60 * 1000; // 6 hours
}
async getTemplates() {
if (
this.templates &&
this.lastFetch &&
Date.now() - this.lastFetch < this.cacheFor
) {
return this.templates;
}
const response = await fetch("https://api.submagic.co/v1/templates", {
headers: { "x-api-key": process.env.SUBMAGIC_API_KEY },
});
const data = await response.json();
this.templates = data.templates;
this.lastFetch = Date.now();
return this.templates;
}
}
```
## Error Responses
```json theme={null}
{
"error": "UNAUTHORIZED",
"message": "Invalid or missing API key"
}
```
```json theme={null}
{
"error": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests",
"retryAfter": 30
}
```
```json theme={null}
{
"error": "INTERNAL_SERVER_ERROR",
"message": "An unexpected error occurred"
}
```
## Default Template
If you don't specify a `templateName` when creating a project, the system will
automatically apply the "Sara" template, which is optimized for general
social media content.
Template names are case-sensitive. Make sure to use the exact template name as
returned by this endpoint.
# Update Project
Source: https://docs.submagic.co/api-reference/update-project
PUT https://api.submagic.co/v1/projects/{id}
Update an existing video project with new settings, features, or media insertions
# Update Project
Update an existing video project with new settings, AI features, or user media (B-roll) insertions. This endpoint allows you to modify project parameters and enhance your video with additional features or custom media content from your library.
This endpoint requires authentication and has a rate limit of 100 requests per
hour. After modifying a video, you'll need to re-export to see the changes
using the export endpoint. When using `removeBadTakes`, the response may take
1-2 minutes as our AI processes the video.
## Authentication
Your Submagic API key starting with `sk-`
## Path Parameters
The unique identifier (UUID) of the project to update
## Request Body
All fields are optional. Only provide the fields you want to update. If a field is provided with a different value from the current project settings, it will be updated.
Automatically remove silence from the video at the specified pace. Allowed
values: `natural`, `fast`, `extra-fast`. - `extra-fast`: 0.1-0.2 seconds of
silence removal - `fast`: 0.2-0.6 seconds of silence removal - `natural`: 0.6+
seconds of silence removal
Automatically detect and remove bad takes and silence from the video using AI
analysis. **Note: This process may take 1-2 minutes to complete.**
Hide captions from the exported video. Optional, defaults to `false`.
Update the animated hook caption shown at the start of the video. Set to
`true` to enable the default AI-generated hook, `false` to remove it, or pass
an object with optional overrides:
* `text`: Custom copy (1-100 characters)
* `template`: Hook title template name (defaults to `"tiktok"`). Fetch valid
names using the [hook title templates
endpoint](/api-reference/hook-title-templates).
* `top`: Vertical position between 0-80 (default `50`)
* `size`: Font size between 0-80 (default `30`)
Template names are validated before processing resumes; invalid names return
`VALIDATION_ERROR` immediately.
Optional background music track. When provided, replaces any existing
background music on the project. The referenced media must be an `AUDIO` type
file in your user media library.
UUID of an audio file from your media library. Must reference a user media
item with type `AUDIO`. Use the [List User Media](/api-reference/list-user-media)
endpoint to find available audio files.
Playback volume (1-100)
Start offset within the audio file in seconds. Defaults to `0`.
Apply a fade-in/fade-out effect to the music track. Defaults to `true`.
Array describing B-roll edits. Provide at least one entry when this field is
included. Each item must include a `type` field to specify whether it's user
media from your library or AI-generated content.
Must be set to `"user-media"`
Start time in seconds where the media should begin (must be ≥ 0)
End time in seconds where the media should end (must be greater than
`startTime`)
UUID of the user media from your library. You can find this ID in the editor's 'B-roll' tab → 'My videos' section under each video.
Layout mode for the inserted media. Available values depend on the media type:
**Video media:** `"cover"`, `"contain"`, `"rounded"`, `"square"`, `"split-50-50"`, `"split-35-65"`, `"split-50-50-bordered"`, `"split-35-65-bordered"`, `"pip-top-right"`, `"pip-bottom-right"`
**Image media:** `"cover"`, `"contain"`, `"rounded"`, `"square"`
Must be set to `"ai-broll"`
Start time in seconds (must be ≥ 0)
End time in seconds. Must be greater than `startTime` and no more than 12
seconds later.
1-2500 character description of what the AI should render. This field is
mandatory whenever `type: "ai-broll"` is present.
Layout mode for the AI B-roll. Allowed values: `"cover"`, `"contain"`, `"rounded"`, `"square"`, `"split-50-50"`, `"split-35-65"`, `"split-50-50-bordered"`, `"split-35-65-bordered"`, `"pip-top-right"`, `"pip-bottom-right"`
**Important:** Each item must have a `type` field. Items entries cannot overlap
each other in time.
## AI B-roll Workflow & Credits
* Every AI B-roll item consumes **3 AI credits**. Requests fail with
`VALIDATION_ERROR` if the authenticated user lacks enough credits (unless their
balance is unlimited).
* When AI B-roll work is accepted, the project is moved back to `processing` and
the generation jobs run asynchronously. Your webhook or polling logic should
watch for the project to return to `completed`.
* Stock/user-media updates continue to succeed immediately with the traditional
`Project updated successfully` message.
## Finding User Media ID
To find your `userMediaId`:
1. Go to the Submagic editor
2. Navigate to the **'B-roll'** tab
3. Add a B-roll to access your media library
4. Go to the **'My videos'** tab
5. Each video will display its unique media ID that you can use with this API
## Response
Success message confirming the project update. When AI B-roll work is accepted,
the message is `Project update accepted` to indicate asynchronous rendering.
The unique identifier of the updated project
Updated processing status of the project. If AI B-roll generation was
requested, the project is moved to `processing` until the renders finish.
## Error Responses
Error code: `NOT_FOUND` or `VALIDATION_ERROR`
Detailed error message explaining what went wrong
```json theme={null}
{
"error": "VALIDATION_ERROR",
"message": "Invalid template name"
}
```
```bash cURL theme={null}
curl -X PUT "https://api.submagic.co/v1/projects/550e8400-e29b-41d4-a716-446655440000" \
-H "x-api-key: sk-your-api-key-here" \
-H "Content-Type: application/json" \
-d '{
"removeSilencePace": "fast",
"removeBadTakes": true,
"hookTitle": {
"text": "New intro in 15 seconds",
"template": "laura",
"top": 40,
"size": 28
},
"music": {
"userMediaId": "88a08eec-712a-45d0-8d0b-3b631700cb3a",
"volume": 25,
"startFromTime": 10,
"fade": true
},
"items": [
{
"type": "user-media",
"startTime": 10.5,
"endTime": 15.2,
"userMediaId": "123e4567-e89b-12d3-a456-426614174000",
"layout": "pip-top-right"
},
{
"type": "ai-broll",
"startTime": 25,
"endTime": 31,
"prompt": "smooth slider shot of a team collaborating in a modern studio",
"layout": "split-50-50"
}
]
}'
```
```javascript JavaScript theme={null}
const updateProject = async (projectId, updateData) => {
const response = await fetch(
`https://api.submagic.co/v1/projects/${projectId}`,
{
method: "PUT",
headers: {
"x-api-key": "sk-your-api-key-here",
"Content-Type": "application/json",
},
body: JSON.stringify(updateData),
}
);
const result = await response.json();
console.log("Project updated:", result.message);
return result;
};
// Usage
const updateData = {
removeSilencePace: "fast",
removeBadTakes: true,
hookTitle: {
text: "New intro in 15 seconds",
template: "laura",
top: 40,
size: 28,
},
music: {
userMediaId: "88a08eec-712a-45d0-8d0b-3b631700cb3a",
volume: 25,
startFromTime: 10,
fade: true,
},
items: [
{
type: "user-media",
startTime: 10.5,
endTime: 15.2,
userMediaId: "123e4567-e89b-12d3-a456-426614174000",
layout: "pip-top-right",
},
{
type: "ai-broll",
startTime: 25,
endTime: 31,
prompt:
"smooth slider shot of a team collaborating in a modern studio",
layout: "split-50-50",
},
],
};
const result = await updateProject(
"550e8400-e29b-41d4-a716-446655440000",
updateData
);
```
```python Python theme={null}
import json
import requests
def update_project(project_id, update_data):
url = f'https://api.submagic.co/v1/projects/{project_id}'
headers = {
'x-api-key': 'sk-your-api-key-here',
'Content-Type': 'application/json'
}
response = requests.put(url, headers=headers, json=update_data)
result = response.json()
print(f"Project updated: {result.get('message')}")
return result
# Usage
update_data = {
'removeSilencePace': 'fast',
'removeBadTakes': True,
'hookTitle': {
'text': 'New intro in 15 seconds',
'template': 'laura',
'top': 40,
'size': 28
},
'music': {
'userMediaId': '88a08eec-712a-45d0-8d0b-3b631700cb3a',
'volume': 25,
'startFromTime': 10,
'fade': True
},
'items': [
{
'type': 'user-media',
'startTime': 10.5,
'endTime': 15.2,
'userMediaId': '123e4567-e89b-12d3-a456-426614174000',
'layout': 'pip-top-right'
},
{
'type': 'ai-broll',
'startTime': 25,
'endTime': 31,
'prompt': 'smooth slider shot of a team collaborating in a modern studio',
'layout': 'split-50-50'
}
]
}
result = update_project('550e8400-e29b-41d4-a716-446655440000', update_data)
```
```json 200 OK - Stock/User Media Only theme={null}
{
"message": "Project updated successfully",
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "processing"
}
```
```json 200 OK - AI B-roll Accepted theme={null}
{
"message": "Project update accepted",
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "processing"
}
```
```json 404 NOT_FOUND theme={null}
{
"error": "NOT_FOUND",
"message": "Project not found"
}
```
```json 400 VALIDATION_ERROR - Invalid Time Range theme={null}
{
"error": "VALIDATION_ERROR",
"message": "endTime must be greater than startTime"
}
```
```json 400 VALIDATION_ERROR - Invalid UUID theme={null}
{
"error": "VALIDATION_ERROR",
"message": "Invalid UUID format for userMediaId"
}
```
**Important:** After updating a project, you must re-export the project to see
the changes in the final video. Use the export endpoint to generate the
updated video with your modifications. **Processing Time:** When using
`removeBadTakes`, the API response may take 1-2 minutes as our AI analyzes and
processes the video to detect and remove bad takes and silence.
# Upload Project
Source: https://docs.submagic.co/api-reference/upload-project
POST https://api.submagic.co/v1/projects/upload
Create a new video project by uploading a video file directly for AI-powered caption generation
# Upload Project
Create a new video project by uploading a video file directly to Submagic. This endpoint accepts multipart/form-data uploads and is ideal for applications where you have video files stored locally or want to upload directly from user devices.
This endpoint requires authentication and has a rate limit of 500 requests per
hour due to the resource-intensive nature of file uploads.
## Authentication
Your Submagic API key starting with `sk-`
## Request Body (multipart/form-data)
A descriptive title for your video project (1-100 characters)
Language code for transcription (e.g., "en", "es", "fr"). Use the [languages
endpoint](/api-reference/languages) to get available options.
Video file to upload. Must be in a supported format and under 2GB.
Name of an AI edit template to apply. AI edit templates automatically apply
AI-powered scene splitting, B-roll, music, and styling to your video. Available templates: `"kelly"` (minimal, design), `"karl"`
(effective, modern), `"ella"` (dynamic, bold).
When `aiEditTemplate` is provided, the only other fields you can pass
alongside it are `title`, `language`, `file`, `webhookUrl`, and `dictionary`.
All other fields will be ignored or rejected.
ID of a saved preset to apply to the project. A preset is a snapshot of your
project settings — including captions style, hook title, music, effects, and
more. When provided, all preset settings are automatically applied after
transcription completes. To get your preset ID, go to the Presets page in the
app, open the dropdown menu on any preset card, and click **"Copy ID"**.
Cannot be combined with `templateName`, `userThemeId`, `hookTitle`, `music`,
`items`, `magicZooms`, `magicBrolls`, `magicBrollsPercentage`,
`removeSilencePace`, or `removeBadTakes` — the preset already controls these
settings.
Template to apply for styling. Use the [templates
endpoint](/api-reference/templates) to get available options. Defaults to
"Sara" if not specified. Cannot be used together with `userThemeId`.
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`. You can find the id of your custom theme by opening a project,
selecting the theme, pressing the pen icon to edit it. You'll see the id of
the theme under its name.
Adds an animated hook caption. Pass `"true"` to enable the default hook, or
pass a JSON string such as
`{"text":"Stop scrolling—watch this in 30 seconds","template":"tiktok","top":45,"size":32}`.
* `text`: Optional custom copy (1-100 characters)
* `template`: Optional template name (defaults to `"tiktok"`). Use the [hook
title templates endpoint](/api-reference/hook-title-templates) to discover
valid names.
* `top`: Optional vertical position between 0-80 (default `50`)
* `size`: Optional font size between 0-80 (default `30`)
Template names are validated before the upload begins; invalid names return a
`VALIDATION_ERROR`.
URL to receive webhook notifications when processing is complete. Must be a
valid HTTPS URL.
JSON array string of custom words or phrases to improve transcription accuracy
(max 100 items, 50 characters each).
JSON string describing optional items to insert into the video. Pass an
array of objects. Each item must include a `type` field to specify whether it's
user media from your library or AI-generated content.
Must equal `"user-media"`
Start time in seconds where the media should begin (≥ 0)
End time in seconds where the media should end (must be greater than
`startTime`)
UUID of the user media from your library. You can find this ID in the
editor's 'B-roll' tab → 'My videos' section under each video.
Layout mode for the inserted media. Available values depend on the media type:
**Video media:** `"cover"`, `"contain"`, `"rounded"`, `"square"`, `"split-50-50"`, `"split-35-65"`, `"split-50-50-bordered"`, `"split-35-65-bordered"`, `"pip-top-right"`, `"pip-bottom-right"`
**Image media:** `"cover"`, `"contain"`, `"rounded"`, `"square"`
Must equal `"ai-broll"`
Start timestamp in seconds (≥ 0)
End timestamp in seconds; must be greater than `startTime` and within a
12-second window
1-2500 character description of the B-roll to generate
Layout mode for the AI B-roll. Allowed values: `"cover"`, `"contain"`, `"rounded"`, `"square"`, `"split-50-50"`, `"split-35-65"`, `"split-50-50-bordered"`, `"split-35-65-bordered"`, `"pip-top-right"`, `"pip-bottom-right"`
**Important:** Each item must have a `type` field. Items entries cannot overlap
in time. When present, this metadata is parsed after the upload completes and
queued for rendering.
Enable automatic zoom effects on the video to enhance visual engagement. Pass
"true" or "false" as string. Optional, defaults to "false".
Enable automatic B-roll insertion to enhance video content with relevant
supplementary footage. Pass "true" or "false" as string. Optional, defaults to
"false".
Percentage of automatic B-rolls to include in the video (0-100). Pass as
string. Only effective when magicBrolls is enabled. Optional, defaults to
"50".
Automatically remove silence from the video at the specified pace. Pass as
string. Optional. Allowed values: "natural", "fast", "extra-fast". -
"extra-fast": 0.1-0.2 seconds of silence removal - "fast": 0.2-0.6 seconds of
silence removal - "natural": 0.6+ seconds of silence removal
Automatically detect and remove bad takes and silence from the video using AI
analysis. Pass "true" or "false" as string. Optional, defaults to "false".
Enable AI-powered audio cleanup that removes background noises from the video.
Pass "true" or "false" as string. Optional, defaults to "false".
Hide captions from the exported video. Pass `"true"` or `"false"` as
string. Optional, defaults to `"false"`.
JSON string describing an optional background music track that spans the full
project duration. The referenced media must be an `AUDIO` type file in your
user media library.
UUID of an audio file from your media library. Must reference a user media
item with type `AUDIO`. Use the [List User Media](/api-reference/list-user-media)
endpoint to find available audio files.
Playback volume (1-100)
Start offset within the audio file in seconds. Defaults to `0`.
Apply a fade-in/fade-out effect to the music track. Defaults to `true`.
## Supported Formats & Limits
* **MP4** (.mp4) - **MOV** (.mov)
* **Max size:** 2GB - **Max duration:** 2 hours
```bash cURL theme={null}
curl -X POST "https://api.submagic.co/v1/projects/upload" \
-H "x-api-key: sk-your-api-key-here" \
-F "title=My Uploaded Video" \
-F "language=en" \
-F "file=@./video.mp4" \
-F "templateName=Hormozi 2" \
-F 'items=[{"type":"user-media","startTime":5,"endTime":10,"userMediaId":"123e4567-e89b-12d3-a456-426614174000","layout":"pip-top-right"},{"type":"ai-broll","startTime":15,"endTime":21,"prompt":"drone shot of a modern city skyline at dusk","layout":"split-50-50"}]' \
-F "webhookUrl=https://yoursite.com/webhook/submagic" \
-F 'dictionary=["Submagic", "AI-powered", "captions"]' \
-F "magicZooms=true" \
-F "magicBrolls=true" \
-F "magicBrollsPercentage=75" \
-F "removeSilencePace=fast" \
-F "removeBadTakes=true" \
-F "cleanAudio=true" \
-F 'hookTitle={"text":"Stop scrolling—watch this in 30 seconds","template":"tiktok","top":45,"size":32}' \
-F 'music={"userMediaId":"88a08eec-712a-45d0-8d0b-3b631700cb3a","volume":30,"startFromTime":0,"fade":true}'
```
```javascript JavaScript theme={null}
const uploadProject = async (file) => {
const formData = new FormData();
formData.append("title", "My Uploaded Video");
formData.append("language", "en");
formData.append("file", file);
formData.append("templateName", "Hormozi 2");
formData.append(
"items",
JSON.stringify([
{
type: "user-media",
startTime: 5,
endTime: 10,
userMediaId: "123e4567-e89b-12d3-a456-426614174000",
layout: "pip-top-right",
},
{
type: "ai-broll",
startTime: 15,
endTime: 21,
prompt: "drone shot of a modern city skyline at dusk",
layout: "split-50-50",
},
])
);
formData.append("webhookUrl", "https://yoursite.com/webhook/submagic");
formData.append(
"dictionary",
JSON.stringify(["Submagic", "AI-powered", "captions"])
);
formData.append("magicZooms", "true");
formData.append("magicBrolls", "true");
formData.append("magicBrollsPercentage", "75");
formData.append("removeSilencePace", "fast");
formData.append("removeBadTakes", "true");
formData.append("cleanAudio", "true");
formData.append(
"hookTitle",
JSON.stringify({
text: "Stop scrolling—watch this in 30 seconds",
template: "tiktok",
top: 45,
size: 32,
})
);
formData.append(
"music",
JSON.stringify({
userMediaId: "88a08eec-712a-45d0-8d0b-3b631700cb3a",
volume: 30,
startFromTime: 0,
fade: true,
})
);
const response = await fetch("https://api.submagic.co/v1/projects/upload", {
method: "POST",
headers: {
"x-api-key": "sk-your-api-key-here",
},
body: formData,
});
const project = await response.json();
console.log("Project uploaded:", 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 uploadProject(file);
}
});
```
```python Python theme={null}
import json
import requests
def upload_project(file_path):
url = 'https://api.submagic.co/v1/projects/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 Uploaded Video',
'language': 'en',
'templateName': 'Hormozi 2',
'items': json.dumps([
{
'type': 'user-media',
'startTime': 5,
'endTime': 10,
'userMediaId': '123e4567-e89b-12d3-a456-426614174000',
'layout': 'pip-top-right'
},
{
'type': 'ai-broll',
'startTime': 15,
'endTime': 21,
'prompt': 'drone shot of a modern city skyline at dusk',
'layout': 'split-50-50'
}
]),
'webhookUrl': 'https://yoursite.com/webhook/submagic',
'dictionary': '["Submagic", "AI-powered", "captions"]',
'magicZooms': 'true',
'magicBrolls': 'true',
'magicBrollsPercentage': '75',
'removeSilencePace': 'fast',
'removeBadTakes': 'true',
'cleanAudio': 'true',
'hookTitle': json.dumps({
'text': 'Stop scrolling—watch this in 30 seconds',
'template': 'tiktok',
'top': 45,
'size': 32
}),
'music': json.dumps({
'userMediaId': '88a08eec-712a-45d0-8d0b-3b631700cb3a',
'volume': 30,
'startFromTime': 0,
'fade': True
})
}
response = requests.post(url, headers=headers, files=files, data=data)
project = response.json()
print(f"Project uploaded: {project['id']}")
return project
# Usage
project = upload_project('./video.mp4')
```
```json 201 Created theme={null}
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"title": "My Uploaded Video",
"language": "en",
"status": "processing",
"webhookUrl": "https://yoursite.com/webhook/submagic",
"templateName": "Hormozi 2",
"magicZooms": true,
"magicBrolls": true,
"magicBrollsPercentage": 75,
"removeSilencePace": "fast",
"removeBadTakes": true,
"cleanAudio": true,
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:30:00.000Z"
}
```
## Error Responses
```json theme={null}
{
"error": "VALIDATION_ERROR",
"message": "File validation failed",
"details": [
{
"field": "file",
"message": "File size exceeds 10GB limit",
"value": null
}
]
}
```
```json theme={null}
{
"error": "PAYLOAD_TOO_LARGE",
"message": "File size exceeds maximum allowed size"
}
```
```json theme={null}
{
"error": "UNSUPPORTED_MEDIA_TYPE",
"message": "Video format not supported"
}
```
```json theme={null}
{
"error": "VALIDATION_ERROR",
"message": "presetId cannot be combined with templateName, userThemeId, magicZooms, magicBrolls, magicBrollsPercentage, removeBadTakes, removeSilencePace, items, hookTitle, or music. The preset controls these settings."
}
```
```json theme={null}
{
"error": "VALIDATION_ERROR",
"message": "Invalid template name"
}
```
# Create User Media
Source: https://docs.submagic.co/api-reference/user-media
POST https://api.submagic.co/v1/user-media
Upload media to your library from a URL for use in video projects as B-roll
# Create User Media
Upload video or image files to your user media library by providing a URL. This media can then be referenced in your video projects using the returned `userMediaId`.
This endpoint requires authentication and has a rate limit of 500 requests per
hour.
## Authentication
Your Submagic API key starting with `sk-`
## Request Body
Public URL to your video or image file. Must be a valid URL and accessible
without authentication.
## Response
Unique identifier for the uploaded media (UUID format). Use this ID to
reference the media in your video projects.
```bash cURL theme={null}
curl -X POST "https://api.submagic.co/v1/user-media" \
-H "x-api-key: sk-your-api-key-here" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/media/sample-video.mp4"
}'
```
```javascript JavaScript theme={null}
const createUserMedia = async (mediaUrl) => {
const response = await fetch("https://api.submagic.co/v1/user-media", {
method: "POST",
headers: {
"x-api-key": "sk-your-api-key-here",
"Content-Type": "application/json",
},
body: JSON.stringify({
url: mediaUrl,
}),
});
const data = await response.json();
console.log("User media created:", data.userMediaId);
return data;
};
// Usage
const media = await createUserMedia(
"https://example.com/media/sample-video.mp4"
);
```
```python Python theme={null}
import requests
def create_user_media(media_url):
url = 'https://api.submagic.co/v1/user-media'
headers = {
'x-api-key': 'sk-your-api-key-here',
'Content-Type': 'application/json'
}
data = {
'url': media_url
}
response = requests.post(url, headers=headers, json=data)
result = response.json()
print(f"User media created: {result['userMediaId']}")
return result
# Usage
media = create_user_media('https://example.com/media/sample-video.mp4')
```
```json 201 Created theme={null}
{
"userMediaId": "88a08eec-712a-45d0-8d0b-3b631700cb3a"
}
```
## Using User Media in Projects
Once you have a `userMediaId`, you can reference it in the `items` array when creating or uploading projects:
```json theme={null}
{
"items": [
{
"type": "user-media",
"startTime": 5,
"endTime": 10,
"userMediaId": "88a08eec-712a-45d0-8d0b-3b631700cb3a",
"layout": "pip-top-right"
}
]
}
```
See the [Create Project](/api-reference/create-project) or [Upload Project](/api-reference/upload-project) documentation for more details on using user media in your videos.
## Error Responses
```json theme={null}
{
"error": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": [
{
"field": "url",
"message": "Must be a valid URL",
"value": "invalid-url"
}
]
}
```
```json theme={null}
{
"error": "VALIDATION_ERROR",
"message": "The following media is not ready yet: df7d8cfd-eb82-4865-8b32-df3396b972b1. Please wait for the upload to complete."
}
```
```json theme={null}
{
"error": "VALIDATION_ERROR",
"message": "URLs from social media platforms are not supported. Please use a direct download link."
}
```
```json theme={null}
{
"error": "UNAUTHORIZED",
"message": "Invalid or missing API key"
}
```
```json theme={null}
{
"error": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests",
"retryAfter": 30
}
```
```json theme={null}
{
"error": "INTERNAL_SERVER_ERROR",
"message": "An unexpected error occurred"
}
```
# Upload User Media
Source: https://docs.submagic.co/api-reference/user-media-upload
POST https://api.submagic.co/v1/user-media/upload
Upload video or image files directly to your media library for use in projects
# Upload User Media
Upload video or image files directly to your user media library. This media can then be referenced in your video projects as custom B-roll using the returned `userMediaId`.
This endpoint requires authentication and has a rate limit of 500 requests per
hour.
## Authentication
Your Submagic API key starting with `sk-`
## Request Body (multipart/form-data)
Video or image file to upload. Must be in a supported format.
## Response
Unique identifier for the uploaded media (UUID format). Use this ID to
reference the media in your video projects.
```bash cURL theme={null}
curl -X POST "https://api.submagic.co/v1/user-media/upload" \
-H "x-api-key: sk-your-api-key-here" \
-F "file=@./my-video.mp4"
```
```javascript JavaScript theme={null}
const uploadUserMedia = async (file) => {
const formData = new FormData();
formData.append("file", file);
const response = await fetch(
"https://api.submagic.co/v1/user-media/upload",
{
method: "POST",
headers: {
"x-api-key": "sk-your-api-key-here",
},
body: formData,
}
);
const data = await response.json();
console.log("User media uploaded:", data.userMediaId);
return data;
};
// Usage with file input
const fileInput = document.getElementById("media-file");
fileInput.addEventListener("change", async (event) => {
const file = event.target.files[0];
if (file) {
await uploadUserMedia(file);
}
});
```
```python Python theme={null}
import requests
def upload_user_media(file_path):
url = 'https://api.submagic.co/v1/user-media/upload'
headers = {
'x-api-key': 'sk-your-api-key-here'
}
with open(file_path, 'rb') as media_file:
files = {
'file': media_file
}
response = requests.post(url, headers=headers, files=files)
result = response.json()
print(f"User media uploaded: {result['userMediaId']}")
return result
# Usage
media = upload_user_media('./my-video.mp4')
```
```json 201 Created theme={null}
{
"userMediaId": "88a08eec-712a-45d0-8d0b-3b631700cb7v"
}
```
## Using User Media in Projects
Once you have a `userMediaId`, you can reference it in the `items` array when creating or uploading projects:
```json theme={null}
{
"items": [
{
"type": "user-media",
"startTime": 5,
"endTime": 10,
"userMediaId": "88a08eec-712a-45d0-8d0b-3b631700cb3a"
}
]
}
```
See the [Create Project](/api-reference/create-project) or [Upload Project](/api-reference/upload-project) documentation for more details on using user media in your videos.
## Error Responses
```json theme={null}
{
"error": "VALIDATION_ERROR",
"message": "File validation failed",
"details": [
{
"field": "file",
"message": "File is required",
"value": null
}
]
}
```
```json theme={null}
{
"error": "VALIDATION_ERROR",
"message": "The following media is not ready yet: df7d8cfd-eb82-4865-8b32-df3396b972b1. Please wait for the upload to complete."
}
```
```json theme={null}
{
"error": "UNAUTHORIZED",
"message": "Invalid or missing API key"
}
```
```json theme={null}
{
"error": "PAYLOAD_TOO_LARGE",
"message": "File size exceeds maximum allowed size"
}
```
```json theme={null}
{
"error": "UNSUPPORTED_MEDIA_TYPE",
"message": "Media format not supported"
}
```
```json theme={null}
{
"error": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests",
"retryAfter": 30
}
```
```json theme={null}
{
"error": "INTERNAL_SERVER_ERROR",
"message": "An unexpected error occurred"
}
```
# Authentication
Source: https://docs.submagic.co/authentication
Learn how to authenticate with the Submagic API using API keys
# Authentication
The Submagic API uses API key authentication to secure access to all endpoints. Every request must include a valid API key in the request headers.
## Getting Your API Key
Sign up for a free account at [app.submagic.co](https://app.submagic.co)
Go to your account settings and click on the "API" tab
Click "Generate API Key" to create your unique API key
Copy your API key immediately - you won't be able to see it again!
**Important**: Your API key will only be shown once during generation. Make
sure to copy and store it securely. If you lose your API key, you'll need to
regenerate a new one.
## API Key Format
Submagic API keys follow this format:
```
sk-[64-character-hex-string]
```
Example:
```
sk-a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456
```
## Authentication Method
Include your API key in the `x-api-key` header with every request:
```http theme={null}
x-api-key: sk-your-api-key-here
```
## Example Requests
```bash cURL theme={null}
curl -X GET "https://api.submagic.co/v1/languages" \
-H "x-api-key: sk-your-api-key-here"
```
```javascript JavaScript theme={null}
const response = await fetch("https://api.submagic.co/v1/languages", {
headers: {
"x-api-key": "sk-your-api-key-here",
},
});
```
```python Python theme={null}
import requests
headers = {
'x-api-key': 'sk-your-api-key-here'
}
response = requests.get('https://api.submagic.co/v1/languages', headers=headers)
```
## API Key Security
Never expose your API key in client-side code, public repositories, or logs
Store your API key in environment variables, not in your source code
## Best Practices
### Environment Variables
Store your API key in environment variables:
```bash .env theme={null}
SUBMAGIC_API_KEY=sk-your-api-key-here
```
```javascript Node.js theme={null}
const apiKey = process.env.SUBMAGIC_API_KEY;
```
```python Python theme={null}
import os
api_key = os.getenv('SUBMAGIC_API_KEY')
```
### Server-Side Only
Never use your API key in client-side JavaScript, mobile apps, or any publicly
accessible code. API keys should only be used from your secure backend
servers.
### Key Rotation
To regenerate your API key:
1. Generate a new API key from your dashboard
2. Update your applications with the new key
3. Test that everything works with the new key
4. The old key will be automatically invalidated
## Authentication Errors
If authentication fails, you'll receive a `401 Unauthorized` response:
```json theme={null}
{
"error": "UNAUTHORIZED",
"message": "Invalid or missing API key"
}
```
If you're having trouble with authentication contact our support team.
# Introduction
Source: https://docs.submagic.co/introduction
Welcome to the Submagic API - Transform your videos with AI-powered captions and effects
# Welcome to the Submagic API
The Submagic API allows you to programmatically generate AI-powered captions for your videos and create engaging short-form clips from YouTube content. Whether you're building a video editing platform, content management system, or automating your video workflow, our API provides enterprise-grade video processing capabilities.
## What can you do with Submagic API?
Generate accurate, styled captions in 100+ languages with automatic
transcription
Automatically generate engaging short-form clips from YouTube videos using
AI
Apply professional templates with animated text, emojis, and visual effects
Transcribe videos in over 100 languages with high accuracy
Receive real-time notifications when your video processing is complete
## Quick Start
Get started with the Submagic API in 3 simple steps:
Sign up for a Submagic account at [https://app.submagic.co/signup](https://app.submagic.co/signup) and
generate your API key from the account settings
Upload a video or provide a video URL to start processing
Download your processed video with captions and effects applied
## Example Request
Here's a quick example of how to create a project with a video URL:
```bash theme={null}
curl -X POST "https://api.submagic.co/v1/projects" \
-H "x-api-key: sk-your-api-key-here" \
-H "Content-Type: application/json" \
-d '{
"title": "My First Video",
"language": "en",
"videoUrl": "https://drive.google.com/video.mp4",
"templateName": "Hormozi 2"
}'
```
## Rate Limits
The Submagic API implements different rate limits based on the type of operation:
* **Lightweight operations** (languages, templates): 1000 requests/hour
* **Standard operations** (project retrieval): 500 requests/hour
* **Upload operations** (project creation, file uploads): 500 requests/hour
* **Export operations** (project export): 50 requests/hour
## Support
Need help getting started? We're here to help:
* 📧 **Email Support**: [support@submagic.co](mailto:support@submagic.co)
* 💬 **Community**: Join our \[Discord] ([https://discord.gg/submagic](https://discord.gg/submagic)) community
* 📖 **Documentation**: Complete API reference and guides
[Generate your API key](https://app.submagic.co/account) and start
transforming your videos with AI
# Rate Limits
Source: https://docs.submagic.co/rate-limits
Understanding Submagic API rate limits and best practices for handling them
# Rate Limits
The Submagic API implements rate limiting to ensure fair usage and maintain service quality for all users. Different endpoints have different rate limits based on their resource intensity.
## Rate Limit Tiers
**1000 requests/hour** - Languages endpoint - Templates endpoint
**500 requests/hour** - Project retrieval
**500 requests/hour** - File upload endpoint, Project creation
## Rate Limit Headers
Every API response includes rate limit information in the headers:
```http theme={null}
HTTP/1.1 200 OK
X-RateLimit-Limit: 30
X-RateLimit-Remaining: 29
X-RateLimit-Reset: 1640995200
```
| Header | Description |
| ----------------------- | ---------------------------------------------- |
| `X-RateLimit-Limit` | Maximum requests allowed in the current window |
| `X-RateLimit-Remaining` | Requests remaining in the current window |
| `X-RateLimit-Reset` | Unix timestamp when the rate limit resets |
## Rate Limit Exceeded
When you exceed the rate limit, you'll receive a `429 Too Many Requests` response:
```json theme={null}
{
"error": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded. Please try again later.",
"retryAfter": 30
}
```
The response includes:
* `retryAfter`: Seconds to wait before making another request
* `Retry-After` header: Same information as `retryAfter` field
## Best Practices
### 1. Implement Exponential Backoff
When you receive a 429 response, implement exponential backoff:
```javascript JavaScript theme={null}
async function makeRequestWithRetry(url, options, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const response = await fetch(url, options);
if (response.status === 429) {
const retryAfter = response.headers.get("Retry-After");
const delay = Math.min(Math.pow(2, attempt) * 1000, 30000);
await new Promise((resolve) => setTimeout(resolve, delay));
continue;
}
return response;
} catch (error) {
if (attempt === maxRetries - 1) throw error;
}
}
}
```
```python Python theme={null}
import time
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
def make_request_with_retry(url, headers, max_retries=3):
session = requests.Session()
retry_strategy = Retry(
total=max_retries,
status_forcelist=[429, 500, 502, 503, 504],
backoff_factor=1
)
adapter = HTTPAdapter(max_retries=retry_strategy)
session.mount("http://", adapter)
session.mount("https://", adapter)
return session.get(url, headers=headers)
```
### 2. Monitor Rate Limit Headers
Track your remaining requests to avoid hitting limits:
```javascript theme={null}
const response = await fetch(endpoint, { headers });
const remaining = parseInt(response.headers.get("X-RateLimit-Remaining"));
const resetTime = parseInt(response.headers.get("X-RateLimit-Reset"));
if (remaining < 5) {
console.log(`Warning: Only ${remaining} requests remaining`);
console.log(`Rate limit resets at: ${new Date(resetTime * 1000)}`);
}
```
### 3. Use Webhooks
Instead of polling for status updates, use webhooks to get notified when processing completes:
```json theme={null}
{
"title": "My Video",
"language": "en",
"videoUrl": "https://example.com/video.mp4",
"webhookUrl": "https://yoursite.com/webhook/submagic"
}
```
## Rate Limit by Endpoint
| Endpoint | Method | Rate Limit |
| --------------------- | ------ | ---------- |
| `/v1/languages` | GET | 1000/hour |
| `/v1/templates` | GET | 1000/hour |
| `/v1/projects/:id` | GET | 100/hour |
| `/v1/projects` | POST | 30/hour |
| `/v1/projects/upload` | POST | 30/hour |
## Increasing Rate Limits
If you need higher rate limits for your application:
Reach out to our support team with your use case details
Share information about your expected request volume and patterns
Our team will review your request and may offer custom rate limits
Consider our enterprise plans for guaranteed higher limits
Contact our team at [support@submagic.co](mailto:support@submagic.co) to
discuss enterprise plans with custom rate limits.