n8n integration
Automate Songs2VID from n8n with the official community package n8n-nodes-songs2vid.
Renders run asynchronously on Songs2VID. Use a webhook (preferred) or poll job status so long FFmpeg jobs never hit HTTP timeouts in your workflow.
Also see the API overview for auth and rate limits.
:::info Cloud vs self-hosted
| Edition | Base URL | API access |
|---|---|---|
| Cloud | https://songs2vid.com | Developer & Automation (€15) or Enterprise |
| Self-hosted OSS | Your origin | Always on — no paid plan |
Independent Producer (€7) is Web UI only — REST calls return 403. :::
Prerequisites
- Songs2VID account with YouTube connected (Google OAuth + YouTube scopes)
- Cloud: Developer & Automation (or Enterprise) — pricing
- An n8n instance that can reach Songs2VID over HTTPS
- Self-hosted n8n: community packages enabled (
N8N_COMMUNITY_PACKAGES_ENABLED=true)
1. Install n8n-nodes-songs2vid
Community nodes UI (recommended)
- Open n8n → Settings → Community nodes
- Install a community node
- Package name:
n8n-nodes-songs2vid
- Confirm and restart n8n if prompted
- On the canvas, search for Songs2VID
npm: npmjs.com/package/n8n-nodes-songs2vid
Manual install (self-hosted)
cd ~/.n8n # or your n8n user folder
npm install n8n-nodes-songs2vid
Restart n8n afterward.
Fallback — HTTP Request nodes
If you cannot install community packages, use n8n’s built-in HTTP Request node against the same /api/v1/* paths (see Endpoints). Auth header:
Authorization: Bearer s2yt_live_your_key_here
2. Create an API key
- Sign in at songs2vid.com
- Open Dashboard → Settings
- Under API access, create a key
- Copy it once — it is shown only at creation and starts with
s2yt_live_
# Optional: create via API (session cookie or existing Bearer key)
curl -X POST "https://songs2vid.com/api/v1/user/api-keys" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"n8n production"}'
Treat API keys like passwords. Revoke from Settings or DELETE /api/v1/user/api-keys?id=....
3. Add credentials in n8n
Songs2VID credential (community node)
- Open any Songs2VID node → Credential to connect with → Create new
- API Key: paste
s2yt_live_…(with or without theBearerprefix) - Base URL:
https://songs2vid.com(no trailing slash), or your self-hosted origin
The node always sends Authorization: Bearer …. There is no X-API-Key header on the server.
Header Auth (HTTP Request fallback)
| Field | Value |
|---|---|
| Name | Authorization |
| Value | Bearer s2yt_live_… |
Attach that credential to every Songs2VID HTTP Request node.
4. Node operations (1:1 with the API)
Every public REST endpoint is mapped. Optional create-render fields (webhook, layout, watermark, playlist, multi-track JSON, …) live under Additional Fields.
| Resource | Operation | Method | Path |
|---|---|---|---|
| Discovery | Get API Catalog | GET | /api/v1 |
| File | Upload | POST | /api/v1/upload |
| Render | Create Render | POST | /api/v1/render |
| Render | Create Job (alias) | POST | /api/v1/jobs |
| Render | Create Batch | POST | /api/v1/jobs/batch |
| Render | Get Render Status | GET | /api/v1/jobs/:id |
| Render | List Renders | GET | /api/v1/jobs or /api/v1/render |
| Playlist | List | GET | /api/v1/playlists |
| Playlist | Create | POST | /api/v1/playlists |
| API Key | List / Create / Delete | GET/POST/DELETE | /api/v1/user/api-keys |
There is no Delete Render endpoint. Finished videos live on YouTube; jobs are not deleted via REST.
Full field reference (metadata, layouts, watermarks, errors): Endpoints.
5. Recommended workflow
flowchart LR
A[New audio in Drive/Dropbox] --> B[Download binary]
B --> C[Upload cover]
B --> D[Upload audio]
C --> E[Create Render]
D --> E
E --> F[Webhook or poll]
F --> G[YouTube live]
With the community node
- Trigger — Google Drive / Dropbox File Created (filter
.mp3/.wav/.flac) - Download binaries into the item (
cover,audio) - Songs2VID → File → Upload — binary property
cover, type Image - Songs2VID → File → Upload — binary property
audio, type Audio - Songs2VID → Render → Create Render
imagePath/audioPath/audioFilenamefrom the upload responses- Privacy / resolution required
- Additional Fields → Webhook URL = your n8n Webhook Production URL
- Separate branch: Webhook node → filter
event→ Discord / Notion / etc.
Equivalent JSON body (HTTP Request)
{
"imagePath": "/uploads/.../cover.jpg",
"webhookUrl": "https://your-n8n.example/webhook/songs2vid-complete",
"items": [
{
"audioPath": "/uploads/.../track.mp3",
"audioFilename": "track.mp3",
"metadata": {
"title": "Artist - Track (Official Audio)",
"songTitle": "Track",
"artist": "Artist",
"privacy": "UNLISTED",
"categoryId": "10",
"resolution": "1920x1080",
"includeWatermark": false
}
}
]
}
Upload a shared cover once and reuse its path, or set a per-track cover via Additional Fields → Per-track Cover Path (metadata.imagePath).
6. Webhooks vs polling
Renders can take minutes. Do not block a single HTTP call waiting for the finished video.
Webhook (preferred)
- Add an n8n Webhook node (POST), path e.g.
songs2vid-complete - Activate the workflow and copy the Production URL
- Pass that URL as
webhookUrlon Create Render - Songs2VID POSTs:
{
"event": "job.completed",
"jobId": "clxxxxxxxx",
"status": "COMPLETED",
"itemId": "clitemxxx",
"youtubeVideoId": "dQw4w9WgXcQ",
"error": null,
"completedAt": "2026-08-08T12:34:56.000Z"
}
event | Meaning |
|---|---|
job.item.completed | One track finished (youtubeVideoId set) |
job.item.failed | One track failed (error set) |
job.completed | All items succeeded |
job.failed | All items failed |
job.partial | Mix of success and failure |
Poll status
Create Render returns:
{
"jobId": "clxxxxxxxx",
"status": "PENDING",
"statusUrl": "/api/v1/jobs/clxxxxxxxx",
"webhookUrl": "https://..."
}
Loop Wait (15–30s) + Get Render Status until COMPLETED, FAILED, or PARTIAL. Prefer webhooks when n8n is reachable from the public internet.
7. Copy-paste workflow template
Import via ⋯ → Import from File / URL, or copy below.
- Create Header Auth (
Authorization=Bearer s2yt_live_…) — or swap HTTP nodes for native Songs2VID nodes after installing the package - Provide binary fields
coverandaudio(or replace the Manual Trigger with Drive/Dropbox) - Set env
SONGS2VID_WEBHOOK_URLto the Webhook node’s Production URL (or paste it into Create Render)
Download workflow JSON · mirror
Preview workflow JSON
{
"name": "Songs2VID — Audio → Render → YouTube (full)",
"meta": {
"templateCredsSetupCompleted": false,
"instanceId": "songs2vid-docs-template-v2"
},
"nodes": [
{
"parameters": {},
"id": "manual-trigger",
"name": "When clicking ‘Test workflow’",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [
0,
0
]
},
{
"parameters": {
"path": "songs2vid-complete",
"httpMethod": "POST",
"responseMode": "onReceived",
"options": {}
},
"id": "webhook-complete",
"name": "Songs2VID Job Webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
0,
360
],
"webhookId": "songs2vid-complete"
},
{
"parameters": {
"content": "## Inputs\nProvide binary fields on the Manual Trigger item (or replace with Drive/Dropbox download):\n- `cover` — JPEG/PNG cover art\n- `audio` — MP3/WAV/FLAC/…\n\nCredential: Header Auth → Name `Authorization`, Value `Bearer s2yt_live_…`",
"height": 220,
"width": 320
},
"id": "sticky-inputs",
"name": "Setup",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-320,
-40
]
},
{
"parameters": {
"method": "POST",
"url": "={{ $env.SONGS2VID_BASE_URL || 'https://songs2vid.com' }}/api/v1/upload",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendBody": true,
"contentType": "multipart-form-data",
"bodyParameters": {
"parameters": [
{
"parameterType": "formBinaryData",
"name": "file",
"inputDataFieldName": "cover"
},
{
"name": "type",
"value": "image"
}
]
},
"options": {}
},
"id": "upload-cover",
"name": "Upload Cover Image",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
280,
0
],
"credentials": {
"httpHeaderAuth": {
"id": "REPLACE_ME",
"name": "Songs2VID API Key"
}
}
},
{
"parameters": {
"method": "POST",
"url": "={{ $env.SONGS2VID_BASE_URL || 'https://songs2vid.com' }}/api/v1/upload",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendBody": true,
"contentType": "multipart-form-data",
"bodyParameters": {
"parameters": [
{
"parameterType": "formBinaryData",
"name": "file",
"inputDataFieldName": "audio"
},
{
"name": "type",
"value": "audio"
}
]
},
"options": {}
},
"id": "upload-audio",
"name": "Upload Audio",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
520,
0
],
"credentials": {
"httpHeaderAuth": {
"id": "REPLACE_ME",
"name": "Songs2VID API Key"
}
}
},
{
"parameters": {
"method": "POST",
"url": "={{ $env.SONGS2VID_BASE_URL || 'https://songs2vid.com' }}/api/v1/render",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"imagePath\": \"{{ $('Upload Cover Image').item.json.path }}\",\n \"webhookUrl\": \"{{ $('Songs2VID Job Webhook').params?.webhookUrl || $env.SONGS2VID_WEBHOOK_URL || '' }}\",\n \"items\": [{\n \"audioPath\": \"{{ $('Upload Audio').item.json.path }}\",\n \"audioFilename\": \"{{ $('Upload Audio').item.json.filename }}\",\n \"metadata\": {\n \"title\": \"{{ $('Upload Audio').item.json.audioTags?.title || $('Upload Audio').item.json.filename }}\",\n \"songTitle\": \"{{ $('Upload Audio').item.json.audioTags?.title || '' }}\",\n \"artist\": \"{{ $('Upload Audio').item.json.audioTags?.artist || '' }}\",\n \"description\": \"Uploaded via n8n + Songs2VID\",\n \"tags\": \"music,songs2vid\",\n \"privacy\": \"UNLISTED\",\n \"categoryId\": \"10\",\n \"resolution\": \"1920x1080\",\n \"notifySubscribers\": false,\n \"madeForKids\": false,\n \"embeddable\": true,\n \"creativeCommons\": false,\n \"includeWatermark\": false\n }\n }]\n}",
"options": {}
},
"id": "create-render",
"name": "Create Render",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
760,
0
],
"credentials": {
"httpHeaderAuth": {
"id": "REPLACE_ME",
"name": "Songs2VID API Key"
}
}
},
{
"parameters": {
"method": "GET",
"url": "={{ $env.SONGS2VID_BASE_URL || 'https://songs2vid.com' }}{{ $json.statusUrl }}",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"options": {}
},
"id": "poll-status",
"name": "Get Render Status (optional poll)",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1000,
0
],
"credentials": {
"httpHeaderAuth": {
"id": "REPLACE_ME",
"name": "Songs2VID API Key"
}
},
"disabled": true
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"id": "job-done",
"leftValue": "={{ $json.body?.event || $json.event }}",
"rightValue": "job.completed",
"operator": {
"type": "string",
"operation": "equals"
}
},
{
"id": "item-done",
"leftValue": "={{ $json.body?.event || $json.event }}",
"rightValue": "job.item.completed",
"operator": {
"type": "string",
"operation": "equals"
}
}
],
"combinator": "or"
},
"options": {}
},
"id": "if-completed",
"name": "Render Completed?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
280,
360
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"id": "job-fail",
"leftValue": "={{ $json.body?.event || $json.event }}",
"rightValue": "job.failed",
"operator": {
"type": "string",
"operation": "equals"
}
},
{
"id": "item-fail",
"leftValue": "={{ $json.body?.event || $json.event }}",
"rightValue": "job.item.failed",
"operator": {
"type": "string",
"operation": "equals"
}
}
],
"combinator": "or"
},
"options": {}
},
"id": "if-failed",
"name": "Render Failed?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
280,
560
]
},
{
"parameters": {
"content": "## YouTube live\nVideo ID: `{{ $json.body?.youtubeVideoId || $json.youtubeVideoId }}`\n\nOpen: https://youtu.be/{{ $json.body?.youtubeVideoId || $json.youtubeVideoId }}\n\nJob: `{{ $json.body?.jobId || $json.jobId }}`",
"height": 200,
"width": 360
},
"id": "sticky-success",
"name": "Success",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
560,
320
]
},
{
"parameters": {
"content": "## Failed\nError: `{{ $json.body?.error || $json.error }}`\n\nPoll: `GET {{ $env.SONGS2VID_BASE_URL || 'https://songs2vid.com' }}/api/v1/jobs/{{ $json.body?.jobId || $json.jobId }}`",
"height": 200,
"width": 360
},
"id": "sticky-fail",
"name": "Failure",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
560,
520
]
}
],
"connections": {
"When clicking ‘Test workflow’": {
"main": [
[
{
"node": "Upload Cover Image",
"type": "main",
"index": 0
}
]
]
},
"Upload Cover Image": {
"main": [
[
{
"node": "Upload Audio",
"type": "main",
"index": 0
}
]
]
},
"Upload Audio": {
"main": [
[
{
"node": "Create Render",
"type": "main",
"index": 0
}
]
]
},
"Create Render": {
"main": [
[
{
"node": "Get Render Status (optional poll)",
"type": "main",
"index": 0
}
]
]
},
"Songs2VID Job Webhook": {
"main": [
[
{
"node": "Render Completed?",
"type": "main",
"index": 0
}
]
]
},
"Render Completed?": {
"main": [
[
{
"node": "Success",
"type": "main",
"index": 0
}
],
[
{
"node": "Render Failed?",
"type": "main",
"index": 0
}
]
]
},
"Render Failed?": {
"main": [
[
{
"node": "Failure",
"type": "main",
"index": 0
}
]
]
}
},
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"staticData": null,
"tags": [
{
"name": "songs2vid"
},
{
"name": "n8n"
},
{
"name": "youtube"
}
],
"triggerCount": 0,
"updatedAt": "2026-08-08T00:00:00.000Z",
"versionId": "2"
}The shipped template uses HTTP Request + Webhook so it works before the community package is installed. After installing n8n-nodes-songs2vid, swap those HTTP nodes for native Songs2VID operations.
Plans, quotas & errors
| Plan | REST API | Monthly renders | Audio encode |
|---|---|---|---|
| Free | No | 3 (Web UI) | 192 kbps AAC |
| Independent Producer (€7) | No — 403 | 30 (Web UI) | 320 kbps |
| Developer & Automation (€15) | Yes | 60 (UI + API) | 320 kbps · priority queue |
| Enterprise | Yes | Custom | 320 kbps · priority queue |
| Status | Meaning |
|---|---|
401 | Missing/invalid API key |
403 | Not on Developer+ · YouTube not connected |
402 | Monthly quota / credits exhausted |
429 | Rate limit (60 req/min per key) — honor Retry-After |
Troubleshooting
| Symptom | Fix |
|---|---|
| Package not found in Community nodes | Confirm the name is exactly n8n-nodes-songs2vid; on self-hosted enable N8N_COMMUNITY_PACKAGES_ENABLED |
| Node missing after install | Restart n8n; hard-refresh the editor |
403 Developer plan message | Upgrade to Developer & Automation; Producer/Free cannot use REST |
403 YouTube not connected | Sign in on the dashboard and reconnect Google with YouTube scopes |
| Webhook never fires | Use the Production webhook URL, activate the workflow, ensure Songs2VID can reach it over HTTPS |
| Upload / create validation errors | Check required fields and enums in Endpoints |
Next steps
- API overview — auth, rate limits, two-step upload
- Endpoints — every
/api/v1/*route with curl - Pricing — Developer & Automation
- npm:
n8n-nodes-songs2vid