# 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 Example.png ## 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.