# Endpoints Set these for the examples below: ```bash export BASE_URL="http://localhost:3000" # local / self-hosted app export API_KEY="s2yt_live_your_key_here" ``` Live HTML docs: [docs.songs2vid.com/docs/api/endpoints](https://docs.songs2vid.com/docs/api/endpoints). ## Discovery ```bash curl "$BASE_URL/api/v1" ``` Returns the endpoint list and requirements (**no auth**). No billing routes are listed or implemented. ## Upload a file (two-step) ```bash curl -X POST "$BASE_URL/api/v1/upload" \ -H "Authorization: Bearer $API_KEY" \ -F "file=@cover.jpg" \ -F "type=image" ``` ```bash curl -X POST "$BASE_URL/api/v1/upload" \ -H "Authorization: Bearer $API_KEY" \ -F "file=@track1.mp3" \ -F "type=audio" ``` ```bash # Optional: PNG watermark logo curl -X POST "$BASE_URL/api/v1/upload" \ -H "Authorization: Bearer $API_KEY" \ -F "file=@logo.png" \ -F "type=logo" ``` ```bash # Optional: custom font (.ttf / .otf, max 10 MB) curl -X POST "$BASE_URL/api/v1/upload" \ -H "Authorization: Bearer $API_KEY" \ -F "file=@Brand.ttf" \ -F "type=font" ``` ### Request fields | Field | Required | Notes | |-------|----------|--------| | `file` | Yes | Multipart file | | `type` | Yes | `image` \| `audio` \| `logo` \| `font` | ### Allowed files | `type` | Formats | Max size | |--------|---------|----------| | `image` | JPEG, PNG, WebP, GIF | 500 MB | | `audio` | MP3, WAV, FLAC | 500 MB | | `logo` | PNG only | 500 MB | | `font` | `.ttf` / `.otf` | **10 MB** | ### Response ```json { "path": "/uploads/.../track.mp3", "filename": "track.mp3", "size": 4123456, "audioTags": { "title": "Song Title", "artist": "Artist Name", "album": "Album", "genre": "Electronic", "year": "2024" } } ``` `audioTags` is present for MP3 when tags are readable; otherwise `null`. ## Create job from paths (recommended) Self-hosted unlocks per-track covers, custom watermarks/fonts, and art-track layouts. Max batch size: **100**. Watermarks are optional — nothing forces the default Songs2VID badge. ```bash curl -X POST "$BASE_URL/api/v1/jobs" \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "imagePath": "/uploads/.../cover.jpg", "items": [{ "audioPath": "/uploads/.../track.mp3", "audioFilename": "track.mp3", "metadata": { "title": "My Artist - My Track (Official Audio)", "songTitle": "My Track", "artist": "My Artist", "description": "", "tags": "electronic", "privacy": "PUBLIC", "categoryId": "10", "resolution": "1920x1080", "notifySubscribers": true, "madeForKids": false, "embeddable": true, "creativeCommons": false, "includeWatermark": true, "layout": { "template": "COVER_LEFT_TEXT_RIGHT", "blurAmount": 60, "blurOpacity": 85, "textPadding": 48, "titleArtistGap": 12, "textOffsetX": 0, "textOffsetY": 0 }, "watermark": { "mode": "default", "fontKey": "montserrat", "position": "bottom-right", "offsetX": 24, "offsetY": 24 }, "playlistId": null } }] }' ``` ### Success response ```json { "jobId": "clxxxxxxxx", "itemCount": 1, "status": "PENDING", "playlist": null } ``` ### Metadata fields | Field | Type | Notes | |-------|------|--------| | `title` | string | YouTube video title | | `songTitle` | string \| null | On-video song title for art-track (max **120**) | | `artist` | string \| null | On-video artist line (max **80**) | | `description` | string | YouTube description | | `tags` | string | Comma-separated | | `privacy` | string | `PUBLIC` \| `PRIVATE` \| `UNLISTED` | | `categoryId` | string | YouTube category ID | | `resolution` | string | See resolutions below | | `notifySubscribers` | boolean | YouTube notify flag | | `madeForKids` | boolean | Made for kids | | `embeddable` | boolean | Allow embedding | | `creativeCommons` | boolean | CC vs standard YouTube license | | `includeWatermark` | boolean | Apply watermark settings | | `imagePath` | string \| null | Per-track cover | | `playlistId` | string \| null | Existing playlist ID | | `layout` | object | Art-track layout | | `watermark` | object | Watermark settings | Snake_case aliases are accepted for layout/watermark fields. ### Resolutions | Value | Aspect | |-------|--------| | `1920x1080` | 16:9 | | `1280x720` | 16:9 | | `854x480` | 16:9 | | `720x720` | 1:1 | | `640x360` | 16:9 | | `426x240` | 16:9 | ### YouTube categories | ID | Name | |----|------| | `1` | Film & Animation | | `2` | Autos & Vehicles | | `10` | Music | | `15` | Pets & Animals | | `17` | Sports | | `19` | Travel & Events | | `20` | Gaming | | `22` | People & Blogs | | `23` | Comedy | | `24` | Entertainment | | `25` | News & Politics | | `26` | Howto & Style | | `27` | Education | | `28` | Science & Technology | | `29` | Nonprofits & Activism | ### Watermark fields `watermark.mode`: `none` | `default` | `text` | `logo` - `none` — no overlay - `default` — built-in Songs2VID badge PNG (`assets/watermark.png`) - `logo` — requires prior `type=logo` upload; set `logoPath` - `text` — custom string (max **80**); set `text` `watermark.position`: `top-left` | `top-right` | `bottom-left` | `bottom-right` | `center` `watermark.offsetX` / `offsetY`: `0`–`200` (default `20`) `watermark.fontKey`: `system` | `inter` | `montserrat` | `roboto` | `oswald` | `playfair` | `custom` (styles art-track text and text watermarks) ### Art-track layouts `metadata.layout.template`: | Enum | Description | |------|-------------| | `COVER_LEFT_TEXT_RIGHT` | Cover left, title & artist right | | `COVER_TOP_TEXT_BOTTOM` | Cover top, title & artist below | | `COVER_RIGHT_TEXT_LEFT` | Cover right, title & artist left | | `CENTERED_COMPACT` | Centered cover + text stack | Fine-tuning (defaults in parentheses): `blurAmount` (55), `blurOpacity` (100), `textPadding` (48), `titleArtistGap` (10), `textOffsetX` (0), `textOffsetY` (0). ## YouTube playlists ```bash curl "$BASE_URL/api/v1/playlists" \ -H "Authorization: Bearer $API_KEY" ``` ```bash curl -X POST "$BASE_URL/api/v1/playlists" \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{"title":"My Album","description":"From Songs2VID","privacy":"unlisted"}' ``` Or pass `createPlaylist` on a job / batch body to create a playlist and attach all videos. ## One-shot batch (small packs only) ```bash curl -X POST "$BASE_URL/api/v1/jobs/batch" \ -H "Authorization: Bearer $API_KEY" \ -F "image=@cover.jpg" \ -F "audio=@track1.mp3" \ -F "audio=@track2.mp3" \ -F 'metadata={"defaults":{"privacy":"PUBLIC"},"items":[{"title":"Track One"},{"title":"Track Two"}]}' ``` Prefer two-step upload + jobs for larger packs. ## Poll job status ```bash curl "$BASE_URL/api/v1/jobs/JOB_ID" \ -H "Authorization: Bearer $API_KEY" ``` ```bash curl "$BASE_URL/api/v1/jobs?limit=10" \ -H "Authorization: Bearer $API_KEY" ``` `GET /api/v1/jobs` accepts `limit` (default **20**, max **100**). ### Job statuses | Status | Meaning | |--------|---------| | `PENDING` | Queued | | `PROCESSING` | Encoding or uploading | | `COMPLETED` | All items succeeded | | `FAILED` | All items failed | | `PARTIAL` | Mix of completed and failed | ### Item statuses | Status | Meaning | |--------|---------| | `PENDING` | Waiting | | `ENCODING` | FFmpeg building video | | `UPLOADING` | Uploading to YouTube | | `COMPLETED` | Live (`youtubeVideoId` set) | | `FAILED` | Failed (`error` message) | YouTube channel daily upload caps are enforced by Google, not Songs2VID. ## HTTP errors | Status | When | |--------|------| | `400` | Validation error | | `401` | Missing or invalid API key | | `403` | YouTube not connected, or request not allowed | | `404` | Job not found | | `429` | API rate limit exceeded | There is no `402` payment / credits status in OSS.