A spreadsheet is a fast place to write a month of posts. Getting them out of it is the part worth automating, but not always with a workflow.
First: is this a batch or a stream?
A batch. You write a month of posts, finish, and want them queued. You don’t need n8n. Export as CSV and import it: BulkPublish’s bulk composer reads a content column plus optional scheduled_at and media_urls. Two minutes, nothing to maintain.
A stream. Rows get added continuously, by several people, and should publish as they appear. That’s what the workflow is for.
Building the workflow for a batch is a permanent maintenance cost for something a file upload does once.
The workflow
1. Schedule Trigger, every 15 or 30 minutes. Prefer this to the new-row trigger, for the reason below.
2. Google Sheets node, read rows.
3. Filter to Approved = yes and Published empty. Two conditions, both necessary.
4. Build the post from the columns.
5. BulkPublish node, Post → Create.
6. Write back to the sheet, setting Published and the post URL.
The row-tracking problem
This is the thing that catches people, and it’s specific to spreadsheets.
n8n’s new-row trigger tracks position. If anyone sorts the sheet, deletes a row, or inserts one in the middle, the tracking no longer matches reality, and you get duplicates, skips, or both. Spreadsheets get reordered constantly, because that’s what they’re for.
The fix is to not rely on position at all: poll on a schedule, filter on a column, and write back after publishing. Then reordering is harmless, because the workflow is reading state from the row rather than counting rows.
The columns
| Column | Purpose |
|---|---|
| Content | The post text |
| Channels | Target platforms |
| Publish at | Scheduled time |
| Media URL | Optional attachment |
| Approved | The gate. yes or nothing |
| Published | Written back after posting |
The Approved column exists because a sheet row is visible to the workflow the moment it’s created, including while someone is still typing it.
Watch the quota
A spreadsheet makes it trivial to paste in fifty rows, and fifty rows is fifty posts.
- Posts: Free: 3/day. Pro: 30/day. Business: unlimited.
- API requests/day: Free: 30. Pro: 5,000. Business: 50,000.
- Channels: Free: 3 (1 per platform). Pro: 30 (2 per platform). Business: 75 (5 per platform).
A workflow polling every 15 minutes spends 96 requests a day before it publishes anything, so the free tier won’t run this.
Check the length
A spreadsheet cell holds far more text than most platforms accept, and nothing in the sheet warns you. Character limits are validated when the post is created, so you’ll get a clear error rather than a truncated post, but the workflow needs to log that somewhere you’ll actually see it.
Draft first
Run it into drafts for the first week. The column mapping is the part that goes wrong, and it’s obvious in a list of drafts.
BulkPublish publishes to 15 platforms: Facebook, Instagram, TikTok, YouTube, X, Threads, Bluesky, Pinterest, Google Business Profile, LinkedIn, Mastodon, Discord, Telegram, Tumblr and Snapchat.
The short version
If the sheet is a finished plan, export it and import the CSV instead of building anything. If it’s a live queue, poll on a schedule and filter on columns rather than using the new-row trigger, because spreadsheets get reordered and position-based tracking produces duplicates. Gate on an Approved column, and write back after publishing.