Four networks, one post each, every day. Miss one evening and the week slips.
Here’s the fix when you automate social media posting with n8n. Each evening, n8n checks tomorrow’s rows in a sheet and hands each one to a posting API with its exact time. On Zernio, four accounts cost $12 a month.
We built it on self-hosted n8n, then broke it on purpose. One retry setting resent posts that had already gone through.
Disclosure: Zernio, n8n and Buffer links are affiliate links. They never change a test result.
What you’re building, and why n8n shouldn’t hold the schedule
Three parts: a queue you write in, an n8n workflow that runs once a day, and a posting API that publishes at the right minute.
Why not let n8n fire each post at its exact time? Because every trigger is a billed execution on n8n Cloud. Polling the sheet every 15 minutes is 96 runs a day, or 2,976 in a 31-day month. The Starter plan includes 2,500.
A once-a-day feeder uses 31. The posting API already knows how to wait until 9:30 tomorrow, so let it.

The split helps when things break, too. n8n never sits on a post overnight, so a restart can’t swallow one. Want AI to draft the captions later? That’s one more node before the checks, wired the way our first AI agent in n8n tutorial shows.
How we tested it (and what we didn’t)
We ran n8n 2.45.4 self-hosted on our Linux box and installed Zernio’s community node, version 0.7.1.
We didn’t point the workflow at a real Zernio account. It called a small stand-in API we wrote from Zernio’s docs, with the same request shape, timezone rules and duplicate checks. That let us break things on purpose without posting junk to anyone’s LinkedIn.
So the n8n behaviour below is what we saw. Zernio’s side follows its documentation. Prices come from Zernio’s and Buffer’s own pricing pages, checked on 31 October 2026.
What you need before you start
Four things, and only one of them can cost money on day one.
- n8n: the free self-hosted Community Edition, or n8n Cloud from €20 a month on annual billing
- A Zernio account with your social accounts connected and an API key. The first two accounts are free, API included
- Your account IDs from Zernio’s accounts endpoint, one per network
- A queue: a Google Sheet or n8n data table with the columns below
| Column | Example | Why it’s there |
|---|---|---|
| id | 101 | Builds the duplicate-proof key |
| date, time | 2026-11-01, 09:30 | Your local time, no maths |
| platforms | linkedin,x | One row, several networks |
| status | ready | Drafts stay put |
| text, x_text | Caption, short X version | X allows 280 characters |
Our lab queue was a six-row CSV file. A Google Sheet works the same way through n8n’s Get Rows operation, and it’s what we’d hand a client.
Build the feeder workflow in six steps
Set aside about 30 minutes. Steps 1 and 4 decide whether a post lands at the right hour.
Build the daily social media feeder
Schedule Trigger to Zernio, with checks in between
-
Step 1 of 6
Set the workflow timezone first
Open the workflow’s Settings and set Timezone to yours, for example
Asia/Kolkata. Skip it and n8n quietly runs on New York time. -
Step 2 of 6
Add a Schedule Trigger for the evening
Add Schedule Trigger and set it to run daily at 19:00. Evening leaves time to fix a flagged row before the first post goes out.
-
Step 3 of 6
Read the queue
Add Google Sheets with Get Rows, or a data table. Each row arrives as one item, with your column names as fields.
-
Step 4 of 6
Add a Code node that builds and checks tomorrow's posts
Paste the code below. It keeps tomorrow’s ready rows, flags long X text and past times, and gives every request an
Idempotency-Key. -
Step 5 of 6
Send the good rows to an HTTP Request node
Add an IF node on
ok. True goes to HTTP Request with the settings below. False goes wherever you’ll see it: Slack, email or a sheet column. -
Step 6 of 6
Test with drafts, then go live
Add
isDraft: trueto the body, run once and check the drafts in Zernio’s dashboard. Remove it and publish the workflow. Tomorrow’s posts now queue tonight.
Here’s the Code node, exactly as we ran it:
// Build tomorrow's posts from the queue, and refuse the ones that would go wrong.
const TZ = 'Asia/Kolkata'; // the timezone your queue is written in
const ACCOUNTS = { linkedin: 'li_acc_01', x: 'x_acc_01', threads: 'th_acc_01' }; // from GET /v1/accounts
const API_NAME = { linkedin: 'linkedin', x: 'twitter', threads: 'threads' };
const tomorrow = $now.setZone(TZ).plus({ days: 1 }).toFormat('yyyy-MM-dd');
const out = [];
for (const { json: row } of $input.all()) {
if (row.status !== 'ready' || row.date !== tomorrow) continue;
const networks = row.platforms.split(',').map(s => s.trim());
const xText = row.x_text || row.text;
const when = DateTime.fromISO(`${row.date}T${row.time}`, { zone: TZ });
const problems = [];
if (networks.includes('x') && xText.length > 280) problems.push(`X text is ${xText.length} characters`);
if (when < $now.plus({ minutes: 15 })) problems.push('time has already passed');
if (problems.length) { out.push({ json: { id: row.id, ok: false, problems } }); continue; }
out.push({ json: {
id: row.id,
ok: true,
idempotencyKey: `queue-${row.id}-${row.date}`,
body: {
content: row.text,
scheduledFor: `${row.date}T${row.time}:00`, // no Z: Zernio reads it in "timezone"
timezone: TZ,
platforms: networks.map(n => ({
platform: API_NAME[n],
accountId: ACCOUNTS[n],
...(n === 'x' && row.x_text ? { customContent: row.x_text } : {}),
})),
metadata: { queueId: row.id },
},
} });
}
return out;
Notice what’s missing: any UTC conversion. Zernio reads a scheduledFor with no Z as wall-clock time in the timezone you send. In our run, 09:30 in Asia/Kolkata came back stored as 04:00 UTC. Correct.
And the HTTP Request node:
Method: POST
URL: https://zernio.com/api/v1/posts
Authentication: Generic Credential Type > Header Auth
Name: Authorization Value: Bearer YOUR_ZERNIO_API_KEY
Send Headers: ON
Idempotency-Key = {{ $json.idempotencyKey }}
Send Body: ON > JSON > Using JSON
{{ JSON.stringify($json.body) }}
Settings: Retry On Fail ON, Max Tries 3, Wait Between Tries 2000 ms
On Error: Continue (using error output)
Keep the key in a credential, not pasted into a field. Our API authentication explainer covers why a Bearer key needs the same care as a password.
On our six-row queue, the first run took 118 milliseconds. Two posts were scheduled and the 338-character X post landed in Needs a fix. The Friday leftover, the Monday post and the draft were skipped.
The retry setting that resent our posts
Retry On Fail retries the whole node, not just the item that failed. We didn’t expect that until we watched it.
Our stand-in API saved the first post, then answered 503. Real servers do that when they time out after writing. n8n waited two seconds and sent both posts again, including the one that had already returned a 201.
| Test | What the API saw | Outcome |
|---|---|---|
| Rerun, with keys | 2 requests, both 200 replays | No new posts |
| First save answers 503, with keys | 503 and 201, then both resent as 200 replays | 2 posts, both marked scheduled |
| Same failure, no keys | 503 and 201, then both resent twice as 409 duplicates | 2 posts exist, both marked failed |
With an Idempotency-Key, the resend is harmless. Zernio matches the key within 24 hours and returns the original post.
Without one, you get the worst of both. Zernio’s duplicate check blocks the second copy with a 409, so nothing double-posts. But n8n reports both posts as failed, and whoever reads that alert will post them again by hand.

On an API with no duplicate check, the same retry would publish twice. Build the key from the row id and date, as the code above does.
Should you use Zernio’s n8n node instead?
For a quick start, yes. For an unattended daily pipeline, we’d use the HTTP Request node, because of what’s missing from the node’s code.
The verified node is friendlier. You pick the profile and account from dropdowns instead of copying IDs. On n8n Cloud you add it from the node panel; self-hosted takes one install from Settings.
Here’s the catch. We searched the source of version 0.7.1: it sends no Idempotency-Key header, so Retry On Fail can trip the problem above. Its Timezone field defaults to UTC. It also covers only 9 networks, with WhatsApp, Telegram, Discord, Slack, Snapchat, Google Business Profile and Reddit missing from its picker.
Pick the node if you post by hand from n8n, or keep Retry On Fail off. Pick HTTP Request if the workflow runs while you sleep, because you control every header and every network the API supports.
Our take: build with the node, run with HTTP Request. Use the node to find your account IDs, then swap it out before you switch on the trigger.
Can you do this with Buffer’s API?
Yes. If your team already plans in Buffer’s calendar, it keeps everything in one place. Three things change in the wiring.
It’s GraphQL, not REST. You send a createPost mutation with the channel ID, mode: customScheduled and a dueAt time in UTC. There’s no timezone field, so the Code node converts first: 09:30 in India becomes 04:00Z.
Errors come back as HTTP 200. Buffer’s docs say GraphQL always returns 200 and puts the failure in the body. n8n will mark a rejected post green, so add an IF node that checks createPost for a message.
createPost has no idempotency key. We checked its input fields in Buffer’s API reference on 31 October 2026 and found none. Keep Retry On Fail off on that node, or one timeout can queue the same post twice.
The free plan suits this setup better than it looks: 3 channels, 10 scheduled posts per channel at a time. A daily feeder only ever queues tomorrow. When you outgrow it, our Buffer discount guide lists the current ways to pay less.
What does automated posting cost each month?
For one creator on four networks, about $12 to $16 a month on Zernio, or $24 on Buffer Essentials billed monthly.
| Setup | Zernio | Buffer Essentials | Buffer Team |
|---|---|---|---|
| Solo: LinkedIn, X, Threads, Instagram (4) | $12 (2 free + 2 × $6) | $24 ($20 yearly) | $48 |
| Agency: 5 clients × 4 networks (20) | $78 | $100 | $160 |
Zernio adds one extra. It passes X’s API fees through at cost: $0.015 per post, but $0.20 for a post with a link. Twenty X posts a month that each link to your blog add $4. Twenty without links add 30 cents.
Then n8n: nothing if you self-host on a server you already run, or €20 a month for Cloud Starter. The feeder’s 31 runs barely dent its 2,500 executions. Our Make vs n8n vs Zapier pricing guide has the full plan maths.
Past 20 accounts the gap widens, because Zernio drops to $3 an account from the 11th. The Zernio pricing breakdown walks through that ladder.
Best for Zernio: developers and agencies who never open a calendar view and want every network on one API. Best for Buffer: a team where someone still edits posts by hand before they go out.
Get an alert when a post fails
Scheduling is half the job. The other half is hearing about the 9:30 post that never went out, before your client does.
Zernio can call an n8n Webhook node when a post fails. Subscribe to post.failed and post.partial only. New to webhooks? Our webhooks explainer shows how n8n receives them.
Two rules from Zernio’s docs shape the receiver. It retries an event up to 7 times unless you answer within 5 seconds, and one event can arrive twice. Our Webhook node answered in 13 to 23 milliseconds. The event’s id is your dedupe key.
Turn on the Webhook node’s Raw Body option, then check the signature in a Code node:
// Verify Zernio's signature, then keep only failures worth waking someone for.
const crypto = require('crypto');
const SECRET = 'lab-webhook-secret'; // the secret you set on the Zernio webhook
const out = [];
for (let i = 0; i < $input.all().length; i++) {
const item = $input.all()[i];
const raw = await this.helpers.getBinaryDataBuffer(i, 'data');
const expected = crypto.createHmac('sha256', SECRET).update(raw).digest('hex');
const got = item.json.headers['x-zernio-signature'] || '';
if (got !== expected) { out.push({ json: { ok: false, reason: 'bad signature' } }); continue; }
const e = JSON.parse(raw.toString('utf8'));
if (!['post.failed', 'post.partial'].includes(e.event)) continue;
const failed = e.post.platforms.filter(p => p.status === 'failed');
out.push({ json: { ok: true, eventId: e.id, queueId: e.post.metadata?.queueId,
alert: `Post ${e.post.metadata?.queueId ?? e.post.id} failed on ${failed.map(p => `${p.platform} (${p.errorCategory})`).join(', ')}` } });
}
return out;
Our first run failed with Module ‘crypto’ is disallowed. Self-hosted n8n blocks built-in modules in the Code node until you set NODE_FUNCTION_ALLOW_BUILTIN=crypto and restart.
After that, a test failure produced “Post 101 failed on linkedin (auth_expired), twitter (auth_expired)”. A forged event signed with the wrong secret was rejected.
Send that line to Slack or email. The errorCategory tells you what to do: auth_expired means reconnect the account, not resend the post.
Three mistakes that break a working feeder
Each one passes a quick test and fails a week later.
Trusting n8n’s default timezone
With no timezone set, n8n uses America/New_York. At 9:09 on a Saturday morning in India, our test workflow believed it was 11:39 PM on Friday. Its tomorrow was a day behind ours.
The JavaScript snippet in Zernio’s own n8n guide has a cousin of this bug. It sets 9:00 on the server’s clock. On our IST machine, that meant 9 AM India time. On a UTC server, the same code schedules 2:30 PM in Mumbai.
Leaving old rows marked ready
Zernio doesn’t reject a scheduledFor that has already passed. It publishes on the spot. A forgotten Friday row goes out the moment the feeder sends it.
That’s why the Code node only takes tomorrow’s rows and refuses any time under 15 minutes away. Write the post ID back to the sheet, too, so a sent row never looks ready again.
Linking images that expire before the post goes out
A scheduled post fetches its media later, sometimes hours later. Buffer downloads the file at publish time. Its docs warn that signed links, like S3 presigned URLs, can expire first and fail the post silently.
Zernio needs the URL reachable until its upload finishes, and its own media upload avoids the issue. Use a plain public HTTPS link that never expires, and test one image row before you load a month.
Frequently asked questions
Can n8n post to Instagram without a posting API?
Yes, through n8n’s Facebook Graph API node and your own Meta developer app. You then handle app review, token refreshes and Instagram’s media rules yourself. A posting API takes that work on. Unless you already run a Meta app, the API is far less to maintain.
Does Zernio’s free plan include API access?
Yes. The first two connected accounts are free with full API access, unlimited posts and webhooks. The limit to watch is requests: 60 a minute on 0 to 2 accounts, rising to 600 from the third account. A daily feeder sending a few posts never comes close.
Can I reuse the same caption on every network?
Yes. Zernio’s duplicate check is keyed on platform, account and content together. One caption can go to LinkedIn, X and Threads in the same request. It returns a 409 only when the same account gets identical content twice within 24 hours.
Can one row go out at different times on each network?
Yes. In Zernio, put a scheduledFor inside a platform entry and it overrides the main time, using the same timezone. One request can hit LinkedIn at 9:00 and Instagram at 18:00. In Buffer, each channel is a separate createPost call anyway.
What happens to scheduled posts if an account disconnects?
Zernio cancels them and sends a post.cancelled webhook, plus account.disconnected for the account itself. Add both events to your alert workflow. You’ll reconnect the account the same day instead of finding a silent gap in your feed a week later.
Will automated posting from n8n get my accounts flagged?
Posting through an official API at a human pace is what every scheduling tool does. Zernio caps each account at 25 posts an hour, with daily caps such as 50 on X and 100 on Instagram. Trouble usually starts with volume and copy-paste spam, not the scheduler.
Do I need to self-host n8n for this?
No. n8n Cloud’s Starter plan runs this feeder easily: about 31 executions a month against its 2,500, plus one per failure alert. Self-hosting is cheaper on a server you already run. You then own the updates, the backups and settings like the crypto allowance.
Your next step
To automate social media posting with n8n reliably, keep n8n as the feeder and let a posting API hold the clock. Zernio fits developers and agencies who want every network on one API. Buffer fits teams who still want a calendar.
One warning: set the workflow timezone and send an Idempotency-Key before you switch on Retry On Fail.
Then load three real posts for tomorrow, run the feeder once with drafts on, and check the times in the dashboard. If they match your sheet, you’re done copy-pasting. New to the tool? Our n8n review covers what it does well and where it fights you.



