Schedule Instagram + Facebook Posts from Node.js in 5 Minutes
A practical step-by-step guide to scheduling social media posts to Instagram and Facebook from a Node.js script using the ViralGhost Agent API. Full code included.
Schedule Instagram + Facebook Posts from Node.js in 5 Minutes
If you're building a Node.js app that needs to post to social media — whether it's a marketing tool, a content pipeline, or just your personal automation — you've probably discovered the hard way that Instagram and Facebook's native APIs are painful.
Between Facebook's Graph API permissions, Instagram's content publishing API requirements, and the constant OAuth token management, what should be a simple "post this text and image" turns into days of integration work.
The ViralGhost Agent API reduces this to a single API call. In this tutorial, you'll go from zero to scheduled posts in about 5 minutes.
What We're Building
A Node.js script that:
1. Reads a content plan from a JSON file
2. Schedules posts to both Instagram and Facebook
3. Supports images, scheduling, and AI-generated captions
By the end, you'll have a reusable script that schedules a full week of social content in one run.
Prerequisites
- Node.js 18+ (we'll use native
fetch)
Step 1: Set Up the Project
bash
mkdir social-scheduler && cd social-scheduler
npm init -y
No dependencies needed — we'll use Node.js built-in fetch and fs.
Step 2: Get Your API Key and Account IDs
After signing up at [viralghost.xyz](https://viralghost.xyz):
1. Link your accounts — Connect Instagram and Facebook from the dashboard
2. Create an API key — Go to Settings → API → Create Key
3. Note your account IDs — Each linked account has an ID like acc_instagram_123
Your API key will look like vg_live_your_api_key. Keep it secret — treat it like a password.
Step 3: Create Your Content Plan
Create a content.json file with your posts for the week:
json
{
"posts": [
{
"text": "Monday motivation: ship something small today 🚀",
"image_url": "https://yoursite.com/images/monday.png",
"day_offset": 0,
"time": "09:00"
},
{
"text": "New blog post: How we reduced our build time by 60% — link in bio",
"image_url": "https://yoursite.com/images/blog-preview.png",
"day_offset": 2,
"time": "12:00"
},
{
"text": "Friday wins thread 🧵 What did you ship this week?",
"day_offset": 4,
"time": "15:00"
},
{
"text": "Weekend project ideas for developers 👇",
"image_url": "https://yoursite.com/images/weekend.png",
"day_offset": 5,
"time": "10:00"
}
]
}
Each post has the caption text, an optional image URL, and when to publish relative to next Monday.
Step 4: Build the Scheduler Script
Create schedule.mjs:
javascript
import fs from "fs";const API_KEY = process.env.VIRALGHOST_API_KEY || "vg_live_your_api_key";
const BASE_URL = "https://api.viralghost.xyz/v1";
// Replace with your actual account IDs from the dashboard
const ACCOUNTS = {
instagram: "acc_instagram_123",
facebook: "acc_facebook_456"
};
async function apiCall(endpoint, body) {
const resp await fetch(${BASE_URL}${endpoint}, {
method: "POST",
headers: {
"Authorization": Bearer ${API_KEY},
"Content-Type": "application/json"
},
body: JSON.stringify(body)
});
const data = await response.json();
if (!response.ok) {
throw new Error(API error: ${data.message} (${data.error}));
}
return data;
}
function getNextMonday() {
const now = new Date();
const m new Date(now);
monday.setDate(now.getDate() + ((1 + 7 - now.getDay()) % 7 || 7));
monday.setHours(0, 0, 0, 0);
return monday;
}
async function scheduleWeek() {
const { posts } = JSON.parse(fs.readFileSync("content.json", "utf-8"));
const m getNextMonday();
console.log(Scheduling ${posts.length} posts starting ${monday.toLocaleDateString()}\n);
for (const post of posts) {
const scheduleDate = new Date(monday);
scheduleDate.setDate(monday.getDate() + post.day_offset);
const [hours, minutes] = post.time.split(":");
scheduleDate.setHours(parseInt(hours), parseInt(minutes), 0, 0);
const payload = {
accounts: [ACCOUNTS.instagram, ACCOUNTS.facebook],
content: { text: post.text },
schedule_at: scheduleDate.toISOString()
};
if (post.image_url) {
payload.content.image_url = post.image_url;
}
try {
const result = await apiCall("/posts", payload);
console.log(✅ Scheduled for ${scheduleDate.toLocaleString()}: "${post.text.substring(0, 50)}...");
console.log( Post ID: ${result.post_id});
console.log( Platforms: Instagram, Facebook\n);
} catch (err) {
console.error(❌ Failed to schedule: ${err.message}\n);
}
}
}
scheduleWeek();
Step 5: Run It
bash
VIRALGHOST_API_KEY=vg_live_your_api_key node schedule.mjs
You'll see output like this:
Scheduling 4 posts starting 3/3/2026✅ Scheduled for 3/3/2026, 9:00:00 AM: "Monday motivation: ship something small today 🚀..."
Post ID: pst_abc123
Platforms: Instagram, Facebook
✅ Scheduled for 3/5/2026, 12:00:00 PM: "New blog post: How we reduced our build time by 60%..."
Post ID: pst_def456
Platforms: Instagram, Facebook
✅ Scheduled for 3/7/2026, 3:00:00 PM: "Friday wins thread 🧵 What did you ship this week?..."
Post ID: pst_ghi789
Platforms: Instagram, Facebook
✅ Scheduled for 3/8/2026, 10:00:00 AM: "Weekend project ideas for developers 👇..."
Post ID: pst_jkl012
Platforms: Instagram, Facebook
That's it. Four posts scheduled to two platforms in one command.
Step 6: Add AI-Generated Captions
Don't want to write every caption by hand? Use the AI generation endpoint:
javascript
async function generateAndSchedule(topic, dayOffset, time) {
// Step 1: Generate a caption
const generated = await apiCall("/ai/generate", {
type: "caption",
prompt: topic,
tone: "casual",
platform: "instagram"
}); console.log(📝 Generated: "${generated.content.substring(0, 60)}...");
// Step 2: Schedule it
const m getNextMonday();
const scheduleDate = new Date(monday);
scheduleDate.setDate(monday.getDate() + dayOffset);
const [hours, minutes] = time.split(":");
scheduleDate.setHours(parseInt(hours), parseInt(minutes), 0, 0);
const result = await apiCall("/posts", {
accounts: [ACCOUNTS.instagram, ACCOUNTS.facebook],
content: { text: generated.content },
schedule_at: scheduleDate.toISOString()
});
console.log(✅ Scheduled for ${scheduleDate.toLocaleString()});
console.log( Post ID: ${result.post_id}\n);
return result;
}
// Schedule a full week with AI-generated content
await generateAndSchedule("Monday motivation for developers", 0, "09:00");
await generateAndSchedule("Promote our latest blog post about build optimization", 2, "12:00");
await generateAndSchedule("Friday wins - ask what people shipped this week", 4, "15:00");
await generateAndSchedule("Weekend side project ideas", 5, "10:00");
Now you're generating AND scheduling content with zero manual copywriting.
Example API Response
Here's what the API returns when you schedule a post:
json
{
"post_id": "pst_abc123",
"status": "scheduled",
"accounts": ["acc_instagram_123", "acc_facebook_456"],
"content": {
"text": "Monday motivation: ship something small today 🚀",
"image_url": "https://yoursite.com/images/monday.png"
},
"scheduled_at": "2026-03-03T09:00:00.000Z",
"created_at": "2026-02-26T17:00:00.000Z"
}
And here's an error response (e.g., invalid account ID):
json
{
"error": "invalid_account",
"message": "Account acc_invalid_123 not found or not linked to your workspace.",
"status": 400
}
Adding to Your Existing App
If you're integrating into an existing Node.js app (Express, Next.js, etc.), wrap the API call in a utility:
javascript
// lib/viralghost.mjs
const API_KEY = process.env.VIRALGHOST_API_KEY;
const BASE_URL = "https://api.viralghost.xyz/v1";export async function schedulePost({ accounts, text, imageUrl, scheduleAt }) {
const payload = {
accounts,
content: { text }
};
if (imageUrl) payload.content.image_url = imageUrl;
if (scheduleAt) payload.schedule_at = scheduleAt;
const res = await fetch(${BASE_URL}/posts, {
method: "POST",
headers: {
"Authorization": Bearer ${API_KEY},
"Content-Type": "application/json"
},
body: JSON.stringify(payload)
});
if (!res.ok) {
const err = await res.json();
throw new Error(err.message);
}
return res.json();
}
// Usage in your Express route:
// import { schedulePost } from "./lib/viralghost.mjs";
// app.post("/api/schedule", async (req, res) => {
// const result = await schedulePost(req.body);
// res.json(result);
// });
Free Tier: What You Get
The [ViralGhost free tier](/docs/agent-api) includes:
No credit card required. If you need more volume, Pro ($49/mo) bumps you to 10 accounts and 500 posts/month.
Next Steps
[Get your free API key at viralghost.xyz](https://viralghost.xyz) and start scheduling posts from Node.js in minutes.
Topics covered:
Share this article: