n8n Social Media Scheduling Automation for Marketing Managers
Automate your social media posting across multiple platforms with this comprehensive n8n workflow designed for marketing professionals
n8n Social Media Scheduling Automation for Marketing Managers
Why This Automation Matters
As a marketing manager, you’re juggling multiple social media platforms, campaigns, and deadlines daily. Manual posting is time-consuming and prone to human error. This n8n automation workflow streamlines your social media scheduling by:
- Saving 10+ hours weekly on manual posting tasks
- Ensuring consistent posting across all platforms simultaneously
- Reducing human error in scheduling and content distribution
- Centralizing content management from a single Google Sheets interface
- Providing automatic backup and logging of all posted content
- Enabling team collaboration through shared scheduling sheets
What You Need Before Starting
Required Accounts & API Access
- n8n Cloud or self-hosted instance
- Google Sheets with API access enabled
- Twitter Developer Account with API v2 access
- Facebook Developer Account with Pages API access
- LinkedIn Company Page with API access
- Buffer or Hootsuite account (optional, for additional platform support)
Prerequisites Setup
- Google Sheets: Create a spreadsheet with columns: Date, Time, Platform, Content, Image_URL, Status
- Social Media APIs: Obtain API keys and tokens for each platform
- n8n Credentials: Configure OAuth connections for Google Sheets and social platforms
- Content Storage: Set up a cloud storage solution (Google Drive/Dropbox) for images
Complete Node-by-Node Build Instructions
Step 1: Schedule Trigger Node
- Add a Schedule Trigger node
- Set trigger rules:
- Rule: “Every 15 minutes”
- Timezone: Your business timezone
- This checks for scheduled posts every 15 minutes
Step 2: Google Sheets Data Retrieval
- Add Google Sheets node
- Configure settings:
- Credential: Your Google Sheets OAuth
- Operation: “Read”
- Document ID: Your scheduling spreadsheet ID
- Sheet: “Schedule”
- Range: “A:F” (covers all data columns)
- This pulls all scheduled content data
Step 3: Filter Pending Posts
- Add Function node named “Filter Ready Posts”
- Insert JavaScript code:
const now = new Date();
const readyPosts = [];
for (const item of $input.all()) {
const scheduleDate = new Date(item.json.Date + ' ' + item.json.Time);
const status = item.json.Status;
if (scheduleDate <= now && status === 'Pending') {
readyPosts.push(item);
}
}
return readyPosts;
Step 4: Platform Router Switch
- Add Switch node
- Configure routing rules:
- Rule 1:
{{ $json.Platform }}equals “Twitter” - Rule 2:
{{ $json.Platform }}equals “Facebook” - Rule 3:
{{ $json.Platform }}equals “LinkedIn” - Rule 4:
{{ $json.Platform }}equals “Instagram”
- Rule 1:
Step 5: Twitter Posting Branch
- Add Twitter node under Route 1
- Configure settings:
- Credential: Twitter API v2 credentials
- Operation: “Create Tweet”
- Text:
{{ $json.Content }} - Media:
{{ $json.Image_URL }}(if provided)
Step 6: Facebook Posting Branch
- Add Facebook Graph API node under Route 2
- Configure settings:
- Credential: Facebook API credentials
- Resource: “Page”
- Operation: “Create Post”
- Page ID: Your Facebook page ID
- Message:
{{ $json.Content }} - Link/Photo:
{{ $json.Image_URL }}
Step 7: LinkedIn Posting Branch
- Add LinkedIn node under Route 3
- Configure settings:
- Credential: LinkedIn API credentials
- Resource: “Company”
- Operation: “Create Company Post”
- Company ID: Your LinkedIn company ID
- Text:
{{ $json.Content }} - Media URL:
{{ $json.Image_URL }}
Step 8: Instagram Posting Branch
- Add HTTP Request node under Route 4
- Configure Instagram Basic Display API call:
- Method: POST
- URL:
https://graph.facebook.com/v17.0/{ig-user-id}/media - Headers: Authorization with Instagram access token
- Body: JSON with caption and image_url
Step 9: Merge Posted Results
- Add Merge node after all platform branches
- Set Mode: “Append”
- This combines results from all posting attempts
Step 10: Update Status in Sheets
- Add Google Sheets node
- Configure settings:
- Operation: “Update”
- Document ID: Same spreadsheet ID
- Sheet: “Schedule”
- Range: Dynamically set based on row
- Data: Update Status column to “Posted”
Step 11: Error Handling
- Add Set node for error logging
- Configure error data capture:
- Platform:
{{ $json.Platform }} - Error:
{{ $json.error }} - Timestamp:
{{ $now }} - Content:
{{ $json.Content }}
- Platform:
Step 12: Success Notification
- Add Slack/Email node for notifications
- Configure success message:
- Message: ”✅ Posted to {{ $json.Platform }}: {{ $json.Content.substring(0, 50) }}…”
- Channel: #marketing-automation
Full Workflow JSON Code Block
{
"name": "Social Media Scheduler",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"field": "minutes",
"minutesInterval": 15
}
]
}
},
"name": "Schedule Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1,
"position": [
240,
300
]
},
{
"parameters": {
"operation": "read",
"documentId": "YOUR_SPREADSHEET_ID",
"sheetName": "Schedule",
"range": "A:F",
"options": {}
},
"name": "Google Sheets",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 3,
"position": [
460,
300
],
"credentials": {
"googleSheetsOAuth2Api": {
"id": "GOOGLE_SHEETS_CREDENTIAL_ID",
"name": "Google Sheets Account"
}
}
},
{
"parameters": {
"functionCode": "const now = new Date();\nconst readyPosts = [];\n\nfor (const item of $input.all()) {\n const scheduleDate = new Date(item.json.Date + ' ' + item.json.Time);\n const status = item.json.Status;\n \n if (scheduleDate <= now && status === 'Pending') {\n readyPosts.push(item);\n }\n}\n\nreturn readyPosts;"
},
"name": "Filter Ready Posts",
"type": "n8n-nodes-base.function",
"typeVersion": 1,
"position": [
680,
300
]
},
{
"parameters": {
"conditions": {
"string": [
{
"value1": "={{ $json.Platform }}",
"value2": "Twitter"
}
]
}
},
"name": "Switch",
"type": "n8n-nodes-base.switch",
"typeVersion": 1,
"position": [
900,
300
]
},
{
"parameters": {
"operation": "tweet",
"text": "={{ $json.Content }}",
"additionalFields": {
"attachments": "={{ $json.Image_URL ? $json.Image_URL : undefined }}"
}
},
"name": "Twitter",
"type": "n8n-nodes-base.twitter",
"typeVersion": 1,
"position": [
1120,
200
],
"credentials": {
"twitterOAuth2Api": {
"id": "TWITTER_CREDENTIAL_ID",
"name": "Twitter API"
}
}
},
{
"parameters": {
"resource": "page",
"operation": "create",
"pageId": "YOUR_FACEBOOK_PAGE_ID",
"content": "={{ $json.Content }}",
"additionalFields": {
"link": "={{ $json.Image_URL }}"
}
},
"name": "Facebook Graph API",
"type": "n8n-nodes-base.facebookGraphApi",
"typeVersion": 1,
"position": [
1120,
320
],
"credentials": {
"facebookGraphApi": {
"id": "FACEBOOK_CREDENTIAL_ID",
"name": "Facebook API"
}
}
},
{
"parameters": {
"resource": "company",
"operation": "create",
"companyId": "YOUR_LINKEDIN_COMPANY_ID",
"text": "={{ $json.Content }}",
"additionalFields": {
"mediaUrl": "={{ $json.Image_URL }}"
}
},
"name": "LinkedIn",
"type": "n8n-nodes-base.linkedIn",
"typeVersion": 1,
"position": [
1120,
440
],
"credentials": {
"linkedInOAuth2Api": {
"id": "LINKEDIN_CREDENTIAL_ID",
"name": "LinkedIn API"
}
}
},
{
"parameters": {
"mode": "append"
},
"name": "Merge",
"type": "n8n-nodes-base.merge",
"typeVersion": 2,
"position": [
1340,
300
]
},
{
"parameters": {
"operation": "update",
"documentId": "YOUR_SPREADSHEET_ID",
"sheetName": "Schedule",
"range": "F{{ $json.row }}",
"options": {
"valueInputMode": "USER_ENTERED"
},
"dataMode": "define",
"fieldsUi": {
"values": [
{
"column": "Status",
"value": "Posted"
}
]
}
},
"name": "Update Status",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 3,
"position": [
1560,
300
],
"credentials": {
"googleSheetsOAuth2Api": {
"id": "GOOGLE_SHEETS_CREDENTIAL_ID",
"name": "Google Sheets Account"
}
}
},
{
"parameters": {
"channel": "#marketing-automation",
"text": "✅ Posted to {{ $json.Platform }}: {{ $json.Content.substring(0, 50) }}...",
"otherOptions": {}
},
"name": "Slack Notification",
"type": "n8n-nodes-base.slack",
"typeVersion": 1,
"position": [
1780,
300
],
"credentials": {
"slackApi": {
"id": "SLACK_CREDENTIAL_ID",
"name": "Slack Workspace"
}
}
}
],
"connections": {
"Schedule Trigger": {
"main": [
[
{
"node": "Google Sheets",
"type": "main",
"index": 0
}
]
]
},
"Google Sheets": {
"main": [
[
{
"node": "Filter Ready Posts",
"type": "main",
"index": 0
}
]
]
},
"Filter Ready Posts": {
"main": [
[
{
"node": "Switch",
"type": "main",
"index": 0
}
]
]
},
"Switch": {
"main": [
[
{
"node": "Twitter",
"type": "main",
"index": 0
}
],
[
{
"node": "Facebook Graph API",
"type": "main",
"index": 0
}
],
[
{
"node": "LinkedIn",
"type": "main",
"index": 0
}
]
]
},
"Twitter": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 0
}
]
]
},
"Facebook Graph API": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 1
}
]
]
},
"LinkedIn": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 2
}
]
]
},
"Merge": {
"main": [
[
{
"node": "Update Status",
"type": "main",
"index": 0
}
]
]
},
"Update Status": {
"main": [
[
{
"node": "Slack Notification",
"type": "main",
"index": 0
}
]
]
}
}
}
FAQ
Q: How do I handle different time zones for global campaigns?
A: Modify the Filter Ready Posts function to include timezone conversion. Add a “Timezone” column to your spreadsheet and use JavaScript’s toLocaleString() with timezone options to convert scheduled times to your workflow’s execution timezone.
Q: What happens if a social media platform API is down when the post is scheduled? A: Add error handling nodes after each platform posting node. Configure them to update the spreadsheet status to “Failed” and send notifications. You can also add a retry mechanism using the Wait node and a loop back to attempt posting again after a delay.
Q: Can I schedule different content variations for the same time slot across platforms? A: Yes, modify your spreadsheet to include platform-specific content columns (Twitter_Content, Facebook_Content, LinkedIn_Content). Update the Switch node routing to use the appropriate content field for each platform while maintaining the same scheduling logic.
Ready to streamline your social media workflow? Start automating with n8n Cloud and transform your marketing efficiency today!
Want the complete prompt pack?
500+ prompts, organised and ready to use.
Download the Full Pack — $19