Most content calendar templates have twenty columns and get abandoned in week three. This one has five, and it imports directly into a scheduler rather than being a plan you then retype.
The template
content,scheduled_at,media_urls
"Big news: our spring update is live.",2026-11-03T09:00,https://example.com/spring.jpg
"Behind the scenes: how we plan a week of content in 20 minutes.",2026-11-03T13:00,https://example.com/planning.jpg
"Three things we learned shipping in public this quarter.",2026-11-04T09:00,
Those three columns are what BulkPublish’s bulk composer reads, and only content is required. You can download a copy with the correct header from the composer, which saves guessing at the column names.
In your own planning sheet you’ll probably want two more for working purposes:
| Column | Type | Purpose |
|---|---|---|
content | Text | The post. Required |
scheduled_at | Date and time | When it publishes |
media_urls | URL | Image or video to attach; several separated by ; |
| Platforms | Text | Working note, set properly at import |
| Status | Text | Idea, drafted, ready |
The last two are for you. They don’t need to import, because channels are better assigned in the composer where you can see the posts.
Why it’s deliberately small
Every column is a field someone has to fill in every time, and a calendar people don’t fill in is worse than no calendar.
The columns most often added and least often used are performance data, which belongs in analytics, and campaign taxonomies, which a label does better. Both make the file wider and the habit harder.
Why channels aren’t in the file
Channel selection is the field you least want to get wrong across a hundred rows and the hardest to sanity-check in a spreadsheet cell.
Assign it after import, in the composer, where the posts are in front of you. You can apply a selection across the whole batch and then override individual posts, which is both faster and safer than a column of comma-separated platform names.
The formatting rules
Almost every failed import is a quoting problem:
- Commas in your text: wrap the field in double quotes
- Double quotes in your text: double them and quote the whole field, so
"He said ""yes""" - Line breaks in a post: quote the field, it parses fine
- Emoji and accents: save as UTF-8
- Dates:
YYYY-MM-DDTHH:MM.02/06/2026is ambiguous and different tools read it differently
Exporting from Google Sheets or Excel handles the quoting. Editing the raw file by hand is where it breaks.
What happens to imperfect rows
- A file with no
contentcolumn is rejected with a message saying so, rather than importing nothing and appearing to work - Rows with no content and no media are skipped, so trailing blank lines don’t become empty posts
- Everything else lands in an editable list
Using it
Plan in the sheet. Export as CSV. Import into the bulk composer. Assign channels. Read the queue in order. Schedule.
- Channels: Free: 3 (1 per platform). Pro: 30 (2 per platform). Business: 75 (5 per platform).
- Posts: Free: 3/day. Pro: 30/day. Business: unlimited.
Check the daily posting limit against your batch size, not the file size. A month of daily posts is 30 posts, and the limit is per day rather than per import.
The short version
Three columns to import, two more for your own working notes, and nothing else. Assign channels after import rather than in the file. Quote anything containing a comma, save as UTF-8, use unambiguous dates. Test with five rows before committing a month to it.