This workflow corresponds to n8n.io template #5683 — we link there as the canonical source.
This workflow follows the HTTP Request → OpenAI recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"id": "CXHBDt6tSiHrMyZj",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "\ud83c\udfac AI YouTube Video Generator - One-Click Automation",
"tags": [
{
"id": "MRQ42hNfYxVuWDnW",
"name": "Selling",
"createdAt": "2025-07-02T18:32:26.295Z",
"updatedAt": "2025-07-02T18:32:26.295Z"
}
],
"nodes": [
{
"id": "sticky-main-info",
"name": "Main Info",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2000,
-400
],
"parameters": {
"width": 389,
"height": 464,
"content": "## \ud83c\udfac AI YouTube Video Generator\n\n**One-Click Automation Workflow**\n\nThis workflow automatically creates complete YouTube videos from a simple text prompt using:\n\n\u2022 **OpenAI** - Content generation & image prompts\n\u2022 **ElevenLabs** - Text-to-speech conversion \n\u2022 **Leonardo AI** - Image & video generation\n\u2022 **Creatomate** - Final video assembly\n\u2022 **Cloudinary** - Asset storage\n\n### \ud83d\ude80 Setup Required:\n1. Configure API credentials for all services\n2. Set Cloudinary environment variable\n3. Customize voice ID in ElevenLabs node\n4. Test with simple prompts first\n\n**Input:** Text query about any topic\n**Output:** Complete YouTube video with audio + visuals"
},
"typeVersion": 1
},
{
"id": "sticky-content-gen",
"name": "Content Generation",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1680,
-450
],
"parameters": {
"width": 289,
"height": 242,
"content": "## \ud83e\udde0 Content Generation\n\n**Step 1:** AI creates video concept\n\n\u2022 Generates structured script (Intro/Base/CTA)\n\u2022 Creates engaging title & description\n\u2022 Optimized for YouTube engagement\n\u2022 Uses latest OpenAI models\n\n**Input:** Your topic query\n**Output:** Complete video concept in JSON"
},
"typeVersion": 1
},
{
"id": "sticky-audio",
"name": "Audio Processing",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1220,
-450
],
"parameters": {
"width": 289,
"height": 242,
"content": "## \ud83c\udf99\ufe0f Audio Processing\n\n**Step 2:** Convert text to speech\n\n\u2022 ElevenLabs high-quality TTS\n\u2022 Returns audio + timing alignment\n\u2022 Segments into 4-second chunks\n\u2022 Optimized for video sync\n\n**Input:** Generated script\n**Output:** Audio file + timing data"
},
"typeVersion": 1
},
{
"id": "sticky-visual",
"name": "Visual Generation",
"type": "n8n-nodes-base.stickyNote",
"position": [
-100,
-450
],
"parameters": {
"width": 349,
"height": 242,
"content": "## \ud83c\udfa8 Visual Generation\n\n**Step 3:** Create video segments\n\n\u2022 AI generates image prompts for each segment\n\u2022 Leonardo AI creates high-quality images\n\u2022 Converts images to animated videos\n\u2022 Each segment matches audio timing\n\n**Process:** Image \u2192 Motion \u2192 Video\n**Output:** Individual video segments"
},
"typeVersion": 1
},
{
"id": "sticky-assembly",
"name": "Final Assembly",
"type": "n8n-nodes-base.stickyNote",
"position": [
2800,
-450
],
"parameters": {
"width": 309,
"height": 242,
"content": "## \ud83c\udfac Final Assembly\n\n**Step 4:** Combine everything\n\n\u2022 Merges all video segments\n\u2022 Adds background audio track\n\u2022 Creates smooth transitions\n\u2022 Renders final MP4 video\n\n**Output:** Complete YouTube-ready video\n**Format:** 1080x1920 (vertical) MP4"
},
"typeVersion": 1
},
{
"id": "sticky-config",
"name": "Configuration",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2000,
120
],
"parameters": {
"width": 309,
"height": 183,
"content": "## \u2699\ufe0f Configuration Notes\n\n**Required Environment Variables:**\n\u2022 `CLOUDINARY_CLOUD_NAME`\n\n**API Credentials Needed:**\n\u2022 OpenAI API key\n\u2022 ElevenLabs API key \n\u2022 Leonardo AI API key\n\u2022 Creatomate API key\n\u2022 Cloudinary credentials"
},
"typeVersion": 1
},
{
"id": "4212a15b-bfc4-4d0f-9b2c-40e7efab0240",
"name": "When clicking \u2018Execute workflow\u2019",
"type": "n8n-nodes-base.manualTrigger",
"position": [
-1640,
-180
],
"parameters": {},
"typeVersion": 1
},
{
"id": "4cc730d1-8da6-4c4a-b099-eed5fd54d41c",
"name": "Ideator \ud83e\udde0",
"type": "@n8n/n8n-nodes-langchain.openAi",
"position": [
-1420,
-180
],
"parameters": {
"modelId": {
"__rl": true,
"mode": "id",
"value": "=o3-mini-2025-01-31"
},
"options": {},
"messages": {
"values": [
{
"role": "system",
"content": "you create video concepts based on the user query.\n\nYou output the following in JSON:\n{\n \"Script\": {\n \"Intro\": \"40-70 characters\",\n \"Base\": \"280-350 characters\",\n \"CTA\": \"\u224850 characters\"\n },\n \"Title\": \"30-50 characters (short, engaging, curiosity-driven e.g. \u201cI tried THIS for 7 days\u2026 Crazy results!\u201d)\",\n \"Description\": \"50-150 characters (punchy hook, add context, CTA, #hashtags)\"\n}\n\n}"
},
{
"content": "<user-query>\nGive me top 5 interesting facts about plastic \n</user-query>"
}
]
},
"jsonOutput": true
},
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.8
},
{
"id": "bb218c67-74bd-40db-9a44-26d37c506fc7",
"name": "Script",
"type": "n8n-nodes-base.set",
"position": [
-1200,
-180
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "d8012b83-168e-4de8-9d4a-dc91e24d9964",
"name": "Script",
"type": "string",
"value": "= {{ $json.message.content.Script.Intro }} {{ $json.message.content.Script.Base }} {{ $json.message.content.Script.CTA }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "1c60fb80-27af-4ceb-81bf-7b66fd8bb420",
"name": "Script Generator",
"type": "n8n-nodes-base.httpRequest",
"position": [
-980,
-180
],
"parameters": {
"url": "https://api.elevenlabs.io/v1/text-to-speech/2qfp6zPuviqeCOZIE9RZ/with-timestamps?output_format=mp3_44100_128",
"method": "POST",
"options": {},
"sendBody": true,
"sendHeaders": true,
"authentication": "genericCredentialType",
"bodyParameters": {
"parameters": [
{
"name": "text",
"value": "={{ $json.Script }}"
},
{
"name": "output_format",
"value": "mp3_44100_128"
},
{
"name": "model_id",
"value": "eleven_multilingual_v2"
}
]
},
"genericAuthType": "httpHeaderAuth",
"headerParameters": {
"parameters": [
{}
]
}
},
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"typeVersion": 4.2
},
{
"id": "85005f99-2e9d-44c4-b52a-13848d2c4137",
"name": "HTTP Request",
"type": "n8n-nodes-base.httpRequest",
"position": [
-760,
-180
],
"parameters": {
"url": "https://prod.0codekit.com/code/python",
"method": "POST",
"options": {
"redirect": {
"redirect": {}
}
},
"sendBody": true,
"sendHeaders": true,
"authentication": "genericCredentialType",
"bodyParameters": {
"parameters": [
{
"name": "code",
"value": "=# -*- coding: utf-8 -*-\n\"\"\"\nn8n \u25b8 \u201cExecute Python\u201d (or \u201cRun Python\u201d) node\nPurpose: slice an ElevenLabs alignment object into fixed-length\n caption / transcript chunks and return them as JSON.\n\nInput (item JSON) must contain:\n \u251c\u2500 audio_base64 \u2013 MP3/WAV in base-64 (not used here but kept in case you want it)\n \u251c\u2500 alignment \u2013 full alignment object from ElevenLabs\n \u2514\u2500 normalized_alignment \u2013 (optional) same structure but already normalised\n\nOutput:\n {\n \"data\": [\n { \"words\": \"Hello world\", \"id\": 1, \"duration\": 3.98 },\n { \"words\": \"\u2026 next chunk \u2026\", \"id\": 2, \"duration\": 4.0 },\n \u2026\n ]\n }\n\"\"\"\nimport json\nfrom typing import List, Dict, Union\n\n# Extract data from N8N context\naudio_base64 = \"{{ $json.audio_base64 }}\"\nalignment_str = r'''{{ JSON.stringify($json.alignment).replace(/\\u2014/g, '-') }}'''\nnormalized_alignment_str = r'''{{ JSON.stringify($json.normalized_alignment).replace(/\\u2014/g, '-') }}'''\n\ndef sanitize_unicode(text: str) -> str:\n \"\"\"Remove problematic Unicode characters that could break JSON parsing.\"\"\"\n return text.encode(\"ascii\", \"ignore\").decode(\"ascii\")\n\ndef parse_alignment_data(alignment_str: str, normalized_str: str) -> tuple:\n \"\"\"Parse and sanitize alignment JSON strings.\"\"\"\n clean_alignment = sanitize_unicode(alignment_str)\n clean_normalized = sanitize_unicode(normalized_str)\n \n alignment = json.loads(clean_alignment or \"{}\")\n normalized_alignment = json.loads(clean_normalized or \"{}\")\n \n return alignment, normalized_alignment\n\n# Parse alignment data\nalignment, normalized_alignment = parse_alignment_data(alignment_str, normalized_alignment_str)\n\n# \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n# 4. Helper: split the aligned characters into 6-second (default) windows\n# \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\ndef create_audio_segments(alignment: Dict, segment_duration: float = 4.0) -> List[Dict[str, Union[str, int, float]]]:\n \"\"\"\n Split aligned audio data into fixed-duration segments optimized for video generation.\n \n Args:\n alignment: Dictionary containing characters and timing data from ElevenLabs\n segment_duration: Target duration for each segment in seconds (default: 4.0)\n \n Returns:\n List of segment dictionaries with words, id, and precise duration\n \"\"\"\n chars = alignment.get(\"characters\", [])\n start_times = [float(t) for t in alignment.get(\"character_start_times_seconds\", [])]\n end_times = [float(t) for t in alignment.get(\"character_end_times_seconds\", [])]\n \n # Validate input data\n if not chars or not start_times or not end_times:\n return []\n \n total_duration = end_times[-1] if end_times else 0\n segments = []\n \n # Handle short audio that fits in one segment\n if 0 < total_duration <= segment_duration:\n return [{\n \"words\": \"\".join(chars).strip(),\n \"id\": 1,\n \"duration\": round(total_duration, 2)\n }]\n \n # Create time-based segments with improved character distribution\n current_time = 0.0\n segment_id = 1\n \n while current_time < total_duration:\n segment_end = min(current_time + segment_duration, total_duration)\n \n # Extract characters within this time window\n segment_chars = [\n char for char, start_time in zip(chars, start_times)\n if current_time <= start_time < segment_end\n ]\n \n # Only add non-empty segments with meaningful content\n if segment_chars:\n segment_text = \"\".join(segment_chars).strip()\n if segment_text: # Ensure we don't add empty segments\n segments.append({\n \"words\": segment_text,\n \"id\": segment_id,\n \"duration\": round(segment_end - current_time, 2)\n })\n segment_id += 1\n \n current_time = segment_end\n \n return segments\n\n# \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n# 5. Run it & hand the result back to n8n\n# \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\ntry:\n segments = create_audio_segments(alignment, segment_duration=4.0)\n result = {\"data\": segments}\nexcept Exception as e:\n # makes debugging in n8n easier\n result = {\"data\": {\"error\": \"Processing failed\", \"details\": str(e)}}\n\n# n8n expects the Python node to expose a variable named \u201cresult\u201d\n# (one per incoming item) \u2013 that\u2019s exactly what we return above.\n"
}
]
},
"genericAuthType": "httpHeaderAuth",
"headerParameters": {
"parameters": [
{}
]
}
},
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"typeVersion": 4.2
},
{
"id": "e0092152-adf3-4feb-9c3a-4a1b9af9aeaf",
"name": "Split Out",
"type": "n8n-nodes-base.splitOut",
"position": [
-540,
-180
],
"parameters": {
"include": "allOtherFields",
"options": {},
"fieldToSplitOut": "result.data"
},
"typeVersion": 1
},
{
"id": "f77297d3-c351-48ff-acf8-fbde536a4dd9",
"name": "image-prompter",
"type": "@n8n/n8n-nodes-langchain.openAi",
"position": [
-320,
-180
],
"parameters": {
"modelId": {
"__rl": true,
"mode": "id",
"value": "o3-mini-2025-01-31"
},
"options": {},
"messages": {
"values": [
{
"role": "system",
"content": "You are an image-prompt generator agent for video production.\nYour job is to turn each script segment into a concise, visually descriptive prompt that will serve as the opening frame of a longer video.\n\nReturn a JSON object for the current script segment only:\n\n{\n \"Prompt\": \"enter prompt here\"\n}\nYour prompt must depict the first frame of the 4-second scene and set the overall visual tone for that shot.\n\nIMPORTANT INSTRUCTIONS\n\nNever include text in the image; on-screen words cannot be animated later.\nKeep the prompt under 240 characters.\n\nMake the image concept extremely simple. The video-generation model struggles with people, complex motion, and busy scenes, but it excels at landscapes, POV shots, and close-ups."
},
{
"content": "=Here's the full script:\n{{ $('Script').item.json.Script }}\n\nHere's the current scene:\nscript portion: {{ $json['result.data'].words }}\nscript position: {{ $json['result.data'].id }}"
}
]
},
"jsonOutput": true
},
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.8
},
{
"id": "f400da48-a2ec-45a9-beb0-61d1bdfe5dd2",
"name": "request image",
"type": "n8n-nodes-base.httpRequest",
"position": [
40,
-180
],
"parameters": {
"url": "https://cloud.leonardo.ai/api/rest/v1/generations",
"method": "POST",
"options": {},
"jsonBody": "={ \"prompt\":\"{{ $json.message.content.Prompt }}\",\n \"modelId\":\"6bef9f1b-29cb-40c7-b9df-32b51c1f67d3\",\n \"width\":832, \"height\":480, \"num_images\":1 }\n",
"sendBody": true,
"contentType": "=json",
"sendHeaders": true,
"specifyBody": "json",
"authentication": "genericCredentialType",
"bodyParameters": {
"parameters": [
{}
]
},
"genericAuthType": "httpHeaderAuth",
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
}
},
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"typeVersion": 4.2
},
{
"id": "b4901763-9973-4fc8-8f3a-f5530ceb23f0",
"name": "Upload Cloudinary",
"type": "n8n-nodes-base.httpRequest",
"position": [
-780,
220
],
"parameters": {
"url": "=https://api.cloudinary.com/v1_1/YOUR_CLOUD_NAME/raw/upload",
"method": "POST",
"options": {},
"sendBody": true,
"contentType": "form-urlencoded",
"bodyParameters": {
"parameters": [
{
"name": "file",
"value": "=data:audio/mp3;base64,{{ $json.audio_base64 }}"
},
{
"name": "upload_preset",
"value": "default"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "3c2434d7-b313-4c05-911e-87028dfcbd9a",
"name": "Aggregate",
"type": "n8n-nodes-base.aggregate",
"position": [
2060,
-180
],
"parameters": {
"include": "specifiedFields",
"options": {},
"aggregate": "aggregateAllItemData",
"fieldsToInclude": "=output",
"destinationFieldName": "Videos"
},
"typeVersion": 1
},
{
"id": "023a1abc-c85f-4e90-bef4-b790a7d29fa2",
"name": "Merge",
"type": "n8n-nodes-base.merge",
"position": [
2300,
0
],
"parameters": {
"mode": "combine",
"options": {
"includeUnpaired": true
},
"combineBy": "combineByPosition"
},
"typeVersion": 3.2
},
{
"id": "9a660895-f394-4758-9213-44db831ee8ad",
"name": "Create editor JSON",
"type": "n8n-nodes-base.httpRequest",
"position": [
2700,
-100
],
"parameters": {
"url": "https://prod.0codekit.com/code/python",
"method": "POST",
"options": {
"redirect": {
"redirect": {}
}
},
"sendBody": true,
"sendHeaders": true,
"authentication": "genericCredentialType",
"bodyParameters": {
"parameters": [
{
"name": "code",
"value": "=#!/usr/bin/env python3\n\"\"\"\nOptimized Video Render JSON Generator for N8N\nCreates Creatomate-compatible render configuration with enhanced video composition\n\nInput: Video segments and audio URL\nOutput: Creatomate render JSON with optimized transitions and timing\n\"\"\"\nimport json\nfrom typing import List, Dict, Union, Any\n\ndef create_video_element(video: Dict, start_time: float, crossfade_duration: float = 0.3) -> Dict[str, Any]:\n \"\"\"\n Create a single video element with optimized animations.\n \n Args:\n video: Video segment data with 'output' URL and 'duration'\n start_time: Start time for this video segment\n crossfade_duration: Duration of crossfade transition\n \n Returns:\n Formatted video element for Creatomate\n \"\"\"\n duration = video.get(\"duration\", 4.0)\n \n return {\n \"type\": \"video\",\n \"source\": video[\"output\"],\n \"duration\": duration,\n \"time\": start_time,\n \"track\": 1,\n \"animations\": [{\n \"type\": \"fade\",\n \"duration\": crossfade_duration,\n \"direction\": \"both\",\n \"transition\": True\n }]\n }\n\ndef build_optimized_render_json(videos: List[Dict], audio_url: str, crossfade_duration: float = 0.3) -> Dict[str, Any]:\n \"\"\"\n Build optimized render JSON for Creatomate with enhanced video composition.\n \n Args:\n videos: List of video segments with URLs and durations\n audio_url: Background audio URL\n crossfade_duration: Duration of crossfade transitions between videos\n \n Returns:\n Complete render configuration for Creatomate API\n \"\"\"\n elements = []\n timeline_position = 0.0\n \n # Add background audio track (spans entire video duration)\n elements.append({\n \"type\": \"audio\",\n \"source\": audio_url,\n \"track\": 0 # Audio on separate track\n })\n \n # Add video segments with optimized transitions\n for i, video in enumerate(videos):\n if not video or \"output\" not in video:\n continue # Skip invalid video segments\n \n video_element = create_video_element(video, timeline_position, crossfade_duration)\n elements.append(video_element)\n \n # Advance timeline by full duration (overlaps handled by crossfade)\n timeline_position += video.get(\"duration\", 4.0)\n \n # Return optimized render configuration\n return {\n \"source\": {\n \"output_format\": \"mp4\",\n \"width\": 1080,\n \"height\": 1920, # Vertical format for social media\n \"frame_rate\": 30, # Smooth playback\n \"elements\": elements\n }\n }\n\ndef main() -> Dict[str, Any]:\n \"\"\"Main function with enhanced error handling and validation.\"\"\"\n try:\n # Parse N8N input data\n videos_json = '{{ JSON.stringify($json.Videos) }}'\n audio_url = \"{{ $json.secure_url }}\"\n \n # Validate inputs\n if not audio_url:\n raise ValueError(\"Audio URL is required\")\n \n videos = json.loads(videos_json) if videos_json else []\n \n if not videos:\n raise ValueError(\"At least one video segment is required\")\n \n # Generate optimized render configuration\n render_config = build_optimized_render_json(videos, audio_url, crossfade_duration=0.3)\n \n return render_config\n \n except json.JSONDecodeError as e:\n return {\"error\": \"Invalid JSON in video data\", \"details\": str(e)}\n except ValueError as e:\n return {\"error\": \"Validation failed\", \"details\": str(e)}\n except Exception as e:\n return {\"error\": \"Render configuration failed\", \"details\": str(e)}\n\n# Execute and return result for N8N\nresult = main()\n"
}
]
},
"genericAuthType": "httpHeaderAuth"
},
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"typeVersion": 4.2
},
{
"id": "667a6ffd-13ef-4673-8cbf-4374fa77dc93",
"name": "SET JSON VARIABLE",
"type": "n8n-nodes-base.set",
"position": [
2920,
-100
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "3028cff7-716e-4f7e-afed-fed7985751c6",
"name": "Creatomate Request",
"type": "object",
"value": "={{ $json.result.source }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "99b1cbb9-98f9-4a32-83d0-c4d756f81cf3",
"name": "Editor",
"type": "n8n-nodes-base.httpRequest",
"position": [
3140,
-100
],
"parameters": {
"url": "https://api.creatomate.com/v1/renders",
"method": "POST",
"options": {},
"sendBody": true,
"sendHeaders": true,
"authentication": "genericCredentialType",
"bodyParameters": {
"parameters": [
{
"name": "source",
"value": "={{ $json['Creatomate Request'] }}"
}
]
},
"genericAuthType": "httpHeaderAuth",
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
}
},
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"typeVersion": 4.2
},
{
"id": "e69860af-ca44-4341-a4a9-65f536b2ff20",
"name": "Rendering wait",
"type": "n8n-nodes-base.wait",
"position": [
3360,
-100
],
"parameters": {
"amount": 70
},
"typeVersion": 1.1
},
{
"id": "8d15aea0-7ca7-41b0-bb44-8b41ecff8eed",
"name": "Get final video",
"type": "n8n-nodes-base.httpRequest",
"position": [
3560,
-100
],
"parameters": {
"url": "=https://api.creatomate.com/v1/renders/{{ $('Editor').item.json.id }}",
"options": {},
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth"
},
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"typeVersion": 4.2
},
{
"id": "940f83fc-ddb0-4e95-a5f9-8cc7840aaf1b",
"name": "request image1",
"type": "n8n-nodes-base.httpRequest",
"maxTries": 5,
"position": [
480,
-180
],
"parameters": {
"url": "={{ \n 'https://cloud.leonardo.ai/api/rest/v1/generations/' + \n ($json.sdGenerationJob?.generationId || $json.generationId || $json.sdGenerationId)\n}}",
"options": {
"response": {
"response": {
"fullResponse": true
}
}
},
"sendHeaders": true,
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth"
},
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"executeOnce": false,
"notesInFlow": false,
"retryOnFail": false,
"typeVersion": 4.2,
"alwaysOutputData": true,
"waitBetweenTries": 5000
},
{
"id": "cafd1f35-2f0c-4c88-87fa-d6f38ada63d8",
"name": "request image2",
"type": "n8n-nodes-base.httpRequest",
"position": [
860,
-180
],
"parameters": {
"url": "https://cloud.leonardo.ai/api/rest/v1/generations-motion-svd",
"method": "POST",
"options": {
"response": {
"response": {
"fullResponse": true
}
}
},
"sendBody": true,
"contentType": "=json",
"sendHeaders": true,
"authentication": "genericCredentialType",
"bodyParameters": {
"parameters": [
{
"name": "=imageId",
"value": "={{$json.body.generations_by_pk.generated_images[0].id}}"
},
{
"name": "=motionStrength",
"value": "={{ 2 }}"
}
]
},
"genericAuthType": "httpHeaderAuth",
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
}
},
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"executeOnce": false,
"typeVersion": 4.2,
"alwaysOutputData": true
},
{
"id": "6ee4427e-b994-4062-9c65-3e6787dc9ec5",
"name": "Request Video",
"type": "n8n-nodes-base.httpRequest",
"position": [
1320,
-180
],
"parameters": {
"url": "=https://cloud.leonardo.ai/api/rest/v1/generations/{{ $json.body.motionSvdGenerationJob.generationId }}",
"options": {
"response": {
"response": {
"fullResponse": true
}
}
},
"sendHeaders": true,
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth"
},
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"typeVersion": 4.2
},
{
"id": "a041856d-e21b-4002-9881-05b085791d33",
"name": "Edit Fields",
"type": "n8n-nodes-base.set",
"position": [
1780,
-180
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "7858977a-d50a-4fad-b0cc-ef787a697f1f",
"name": "output",
"type": "string",
"value": "={{ $json.body.generations_by_pk.generated_images[0].motionMP4URL }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "dc0d72a4-7ca0-4597-a912-daa03f683525",
"name": "Wait3",
"type": "n8n-nodes-base.wait",
"position": [
660,
-180
],
"parameters": {
"unit": "minutes",
"amount": 1
},
"typeVersion": 1.1
},
{
"id": "84a66ca3-e7a6-49d2-adae-4c215e01cf08",
"name": "Wait1",
"type": "n8n-nodes-base.wait",
"position": [
1540,
-180
],
"parameters": {
"unit": "minutes",
"amount": 1
},
"typeVersion": 1.1
},
{
"id": "a3c77e5a-40b4-460d-af50-37ced1bd78c1",
"name": "Wait2",
"type": "n8n-nodes-base.wait",
"position": [
260,
-180
],
"parameters": {
"unit": "minutes",
"amount": 1
},
"typeVersion": 1.1
},
{
"id": "a3fd3ed3-31ce-4898-9a38-acf528727882",
"name": "Wait4",
"type": "n8n-nodes-base.wait",
"position": [
1080,
-180
],
"parameters": {
"unit": "minutes",
"amount": 2
},
"typeVersion": 1.1
}
],
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "552e189c-3d6c-46fd-aa9c-96af0515dde8",
"connections": {
"Merge": {
"main": [
[
{
"node": "Create editor JSON",
"type": "main",
"index": 0
}
]
]
},
"Wait1": {
"main": [
[
{
"node": "Edit Fields",
"type": "main",
"index": 0
}
]
]
},
"Wait2": {
"main": [
[
{
"node": "request image1",
"type": "main",
"index": 0
}
]
]
},
"Wait3": {
"main": [
[
{
"node": "request image2",
"type": "main",
"index": 0
}
]
]
},
"Wait4": {
"main": [
[
{
"node": "Request Video",
"type": "main",
"index": 0
}
]
]
},
"Editor": {
"main": [
[
{
"node": "Rendering wait",
"type": "main",
"index": 0
}
]
]
},
"Script": {
"main": [
[
{
"node": "Script Generator",
"type": "main",
"index": 0
}
]
]
},
"Aggregate": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 0
}
]
]
},
"Split Out": {
"main": [
[
{
"node": "image-prompter",
"type": "main",
"index": 0
}
]
]
},
"Edit Fields": {
"main": [
[
{
"node": "Aggregate",
"type": "main",
"index": 0
}
]
]
},
"HTTP Request": {
"main": [
[
{
"node": "Split Out",
"type": "main",
"index": 0
}
]
]
},
"Ideator \ud83e\udde0": {
"main": [
[
{
"node": "Script",
"type": "main",
"index": 0
}
]
]
},
"Request Video": {
"main": [
[
{
"node": "Wait1",
"type": "main",
"index": 0
}
]
]
},
"request image": {
"main": [
[
{
"node": "Wait2",
"type": "main",
"index": 0
}
]
]
},
"Rendering wait": {
"main": [
[
{
"node": "Get final video",
"type": "main",
"index": 0
}
]
]
},
"image-prompter": {
"main": [
[
{
"node": "request image",
"type": "main",
"index": 0
}
]
]
},
"request image1": {
"main": [
[
{
"node": "Wait3",
"type": "main",
"index": 0
}
]
]
},
"request image2": {
"main": [
[
{
"node": "Wait4",
"type": "main",
"index": 0
}
]
]
},
"Script Generator": {
"main": [
[
{
"node": "HTTP Request",
"type": "main",
"index": 0
},
{
"node": "Upload Cloudinary",
"type": "main",
"index": 0
}
]
]
},
"SET JSON VARIABLE": {
"main": [
[
{
"node": "Editor",
"type": "main",
"index": 0
}
]
]
},
"Upload Cloudinary": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 1
}
]
]
},
"Create editor JSON": {
"main": [
[
{
"node": "SET JSON VARIABLE",
"type": "main",
"index": 0
}
]
]
},
"When clicking \u2018Execute workflow\u2019": {
"main": [
[
{
"node": "Ideator \ud83e\udde0",
"type": "main",
"index": 0
}
]
]
}
}
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
httpHeaderAuthopenAiApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow creates an engaging YouTube Short with a single click — from script to voiceover, to visuals and background music. It combines several AI tools to automate content creation and final video assembly. Accepts an input prompt or topic Generates script using GPT…
Source: https://n8n.io/workflows/5683/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Ask questions like “How much did I spend on food last month?” and get instant answers from your financial data — directly in Telegram.
The Problem That it Solves
This intelligent email automation workflow helps you maximize engagement through domain-based outreach. It utilizes AI-powered personalization and strategic follow-ups to increase response rates. The
Note: Now includes an Apify alternative for Rapid API (Some users can't create new accounts on Rapid API, so I have added an alternative for you. But immediately you are able to get access to Rapid AP
Scrape ads – Pulls Facebook Ad Library data for "ai automation" keywords using Apify Filter & sort – Filters ads by page likes (>1,000) and separates into videos, images, and text ads Analyze creat