You can have a hands-off pipeline that (1) pulls the latest headlines from a niche RSS feed, (2) turns a short summary into a spoken script with OpenAI GPT-4 and ElevenLabs voice synthesis, (3) stitches the audio onto a static visual template with FFmpeg, and (4) uploads the resulting 60-second video to YouTube Shorts - all orchestrated in n8n. After a few hours of setup the bot runs on its own, delivering fresh, faceless content every day.
What you need
| Tool | Plan / Price | Role |
|---|---|---|
| n8n (cloud) | Free tier → 2,000 workflow executions / month; paid plans start at $20/mo | Orchestrator, webhook handling, API calls |
| ElevenLabs | Free tier → 20,000 characters / month; paid "Pro" ≈ $17/mo for 250,000 characters | Text-to-speech voice generation |
| OpenAI (ChatGPT) | Free trial $5 credit; pay-as-you-go $0.02 per 1 K tokens for gpt-3.5-turbo | Prompt engineering, headline summarisation |
| Google Cloud Project (YouTube Data API) | Free tier → 10,000 quota units / day; additional units $0.01 per 1 K units | Upload Shorts, set metadata |
| FFmpeg (CLI) | Free, open-source | Assemble audio + background image into a 9:16 MP4 |
| Static visual template (PNG) | One-off design, e.g., 1080×1920 branding image | Visual background for every short |
| Optional: Docker / VPS | Free (self-hosted) or $5-$10/mo for a small VPS | Run n8n self-hosted if you prefer total control |
Estimated build time: 4-6 hours (including API key creation and testing).
A single n8n workflow on the free tier can publish up to 30 Shorts per day without exceeding YouTube's 10 000-unit daily quota.
Step-by-step build
1. Prepare the infrastructure
- Create a Google Cloud project and enable the YouTube Data API v3. Generate an OAuth 2.0 client ID (Web application) and download the JSON credentials. The API costs 50 units for a
videos.insertcall - well within the 10 000-unit daily free quota. - Sign up for ElevenLabs (https://elevenlabs.io) and locate your API key in the dashboard. The free tier gives you 20 000 characters of speech per month, enough for ~30 seconds of audio per video.
- Create an OpenAI API key at https://platform.openai.com/account/api-keys. Use the
gpt-3.5-turbomodel for best cost-performance. - Provision n8n: sign up for the free cloud plan at https://n8n.io, or spin up the Docker image (
docker run -d --name n8n -p 5678:5678 n8nio/n8n). Keep the instance running 24/7.
2. Build the n8n workflow skeleton
Open the n8n editor and click New workflow. Name it Faceless News Shorts.
- Webhook node - receives a scheduled trigger. Set HTTP Method to
GETand the Path to/trigger-news. - Cron node - schedule the webhook to fire every 6 hours (four times a day). Set Cron Expression to
0 */6 * * *. Connect the Cron node to the Webhook node with a Execute after link.
3. Pull niche news via RSS
Add an HTTP Request node after the Webhook.
- Method:
GET - URL:
https://example.com/niche-feed.rss(replace with your niche RSS) - Response Format:
XML
The node returns an XML document. Use a Set node to extract the first <item> using an expression:
Now you have fields title, link, and description.
4. Summarise the article with OpenAI
Insert an OpenAI node (available in n8n's node library).
- Operation:
Chat Completion - Model:
gpt-3.5-turbo - Prompt:
Set Temperature to 0.7. The node returns a summary field containing the script.
5. Generate audio with ElevenLabs
Add an HTTP Request node for ElevenLabs TTS.
- Method:
POST - URL:
https://api.elevenlabs.io/v1/text-to-speech/{{ $json["voice_id"] }}/stream - Headers:
- Body (JSON):
Check the Response Format is File. The node streams an MP3 file (audio.mp3) to the next step.
6. Stitch audio onto a static visual template
Because n8n has no native video encoder, we call a tiny Docker-based FFmpeg micro-service. Deploy it on the same host (or use a public endpoint like https://ffmpeg-api.example.com).
Add another HTTP Request node:
- Method:
POST - URL:
https://ffmpeg-api.example.com/compose - Headers:
Content-Type: multipart/form-data - Form Data:
| Field | Value |
|---|---|
| image | (Upload your 1080×1920 PNG template) |
| audio | (Select the audio.mp3 output from the previous node) |
| duration | 60 |
| output_format | mp4 |
The service returns video.mp4 (9:16, 60 seconds).
Tip: If you run FFmpeg locally, the equivalent command is:
7. Upload to YouTube Shorts
Add the YouTube node (n8n marketplace) configured with the OAuth credentials from step 1.
- Operation:
Upload Video - Video File:
{{$node["FFmpeg"].json["video.mp4"]}} - Title:
{{ $node["OpenAI"].json["summary"] | slice:0:50 }}... - Description:
Automatically generated short from {{ $node["HTTP Request (RSS)"].json["title"] }} - Tags:
news,shorts,{{ $node["HTTP Request (RSS)"].json["category"] || "niche" }} - Privacy Status:
public
The YouTube API automatically marks videos with a 9:16 aspect ratio and ≤ 60 seconds as Shorts.
8. Add error handling and notifications
Create a IF node that checks the HTTP status of the ElevenLabs request ({{ $node["ElevenLabs"].json["statusCode"] === 200 }}).
- True branch: continue to FFmpeg.
- False branch: send a Slack or email alert via the Email node, attaching the error payload.
Finally, add a Set node at the end to log the successful upload timestamp to a Google Sheet (or Airtable) for future analytics.
9. Activate and test
- Click Activate in n8n.
- Manually invoke the webhook (
GET https://YOUR_N8N_DOMAIN/webhook/trigger-news) to see the whole chain run once. - Verify the video appears in your YouTube Shorts feed within a minute.
If everything works, the cron schedule will fire automatically every six hours, delivering fresh, faceless content without any manual intervention.
faceless ai content automation: common failure points
| Failure mode | Symptom | Fix |
|---|---|---|
| YouTube quota exhaustion | API returns quotaExceeded (HTTP 403). | Each videos.insert costs 50 units. Stay under 10 000 units/day by limiting uploads to ≤ 200 Shorts. If you need more, request a quota increase in the Google Cloud console. |
| ElevenLabs character limit | Audio node returns 429 Too Many Requests. | Monitor X-RateLimit-Remaining header; the free tier caps at 20 000 characters per month. Reduce script length or upgrade to the $17 Pro plan. |
| OpenAI token overrun | Unexpected high cost or invalid_request_error. | Keep prompts under 1 000 tokens total. Use gpt-3.5-turbo ( $0.02 per 1 K tokens ) and enable the maxTokens field at 500. |
| n8n execution cap | Workflow stops after 2 000 runs. | The free tier provides 2 000 executions/month. Either increase the schedule (e.g., every 12 hours) or move to a paid plan ($20/mo) which offers 20 000 executions. |
| FFmpeg service unavailable | HTTP 502 from ffmpeg-api.example.com. | Deploy your own FFmpeg container (docker run -p 8080:80 ffmpeg/ffmpeg:latest) and point the n8n node to http://localhost:8080/compose. |
| Invalid RSS structure | No <item> extracted, script is empty. | Use an XML Parse node to map the exact path, or switch to a more stable JSON API if the feed is malformed. |
Warning: Missing any of the OAuth refresh token steps will cause the YouTube upload to fail after the first hour; always store the refresh token securely (e.g., n8n secret).
Where this breaks
Even with the checklist above, real-world deployments hit snags. The most frequent culprits are token expiration (Google refresh tokens can become invalid after 6 months of inactivity) and the brittle nature of RSS parsing - some sites embed HTML entities that break the OpenAI prompt. To mitigate:
- Automate token refresh: add a Google OAuth2 Refresh Token node that runs daily and updates the stored credential.
- Sanitise RSS content: before sending to OpenAI, pipe the description through a Function node that strips HTML tags using a regex (
{{ $json["description"].replace(/<[^>]*>/g, "") }}). - Cost monitoring: create a simple Google Sheet that logs OpenAI token usage (
{{ $node["OpenAI"].json["usage"]["total_tokens"] }}) and ElevenLabs character count. Set a conditional alert when you reach 80 % of the free quota.
By building in these safeguards, the bot stays alive for months without manual rescue.
For a deeper technical reference, see n8n's documentation.