Resources · Feature guides

CSV bulk import

Import a full season's fixtures, a year of classes, or a migrated archive from another calendar plugin — in one upload. Site Calendar Pro's CSV importer handles up to 500 events at a time, creates missing venues and organizers on the fly, attaches categories, and reports row-by-row exactly what worked and what didn't.

When CSV import is the right answer

Manually creating events through the editor takes about 2 minutes each once you know the meta boxes. That's fine for one or two events a week. It's misery when you're populating a year of cricket fixtures (50 dates), a season of weekly classes (40 dates), or migrating from another plugin (potentially hundreds).

The CSV importer turns that into a single upload. You build your event list in Excel, Numbers, Google Sheets, or any tool that exports CSV, save the file, drag it into the import screen, and Site Calendar creates every event in one batch. Venues and organizers referenced by name are auto-created if they don't already exist. Categories are matched by name and auto-assigned.

How it works under the hood. The importer reads your CSV row by row, validates each row against the schema (start date required, title required, etc.), then calls the same wp_insert_post functions the manual editor uses. Each imported event becomes a real pcal_event post indistinguishable from a manually created one — you can edit it later, attach tickets, mark it recurring, anything. The CSV doesn't store any state of its own; the events are first-class WordPress records from the moment they're created.

Before you start

Make sure each of the following is true.

  • Site Calendar Pro 1.1.1 or later is active (the importer was added in 1.1.1).
  • Your Pro licence is active (see Activating your Pro licence).
  • You have a spreadsheet tool that can export CSV (Excel, Numbers, Google Sheets, LibreOffice Calc, Airtable, or any equivalent).
  • You know which venues and organizers your events use. If they don't already exist in Site Calendar, you can either create them manually first or let the importer create them on the fly from the venue/organizer names in your CSV.
  • You have an idea of how many events you're importing. The hard limit is 500 events per upload — for larger migrations, split the file into multiple batches by date range or category.

Step 1 — Open the CSV import screen

In your WordPress admin, go to Site Calendar → Import. You'll see the importer interface with three main areas: a download link for the template, a file upload area, and the post-import results table (empty on first visit).

[Screenshot: Site Calendar → Import screen showing the template download link, drag-and-drop file area, and Import button]
One screen, three steps — download template, fill it in, upload.

Click Download CSV template to grab the empty template file. This file has the exact column headers Site Calendar expects, in the exact order it expects them. Always start from this template rather than building a CSV from scratch — saves hours of debugging.


Step 2 — Understand the column structure

The template has these columns. The required ones (only two!) are marked; everything else is optional and can be left blank.

Required columns

  • title (required) — the event's title, shown in calendar views and as the page heading
  • start_date (required) — when the event begins, in YYYY-MM-DD format (e.g. 2026-09-14)

Common optional columns

  • start_time — in HH:MM 24-hour format (e.g. 19:30). Leave blank for all-day events.
  • end_date — for multi-day events. Same format as start_date. Leave blank for same-day events.
  • end_time — same format as start_time
  • all_day — set to 1 to mark as an all-day event, or 0 / blank for timed events
  • description — the event description body. Plain text or basic HTML. Becomes the single-event page's main content.
  • venue — venue name as a string. If a venue with that exact name already exists, the importer reuses it; if not, it creates a new venue with this name.
  • venue_address — the venue's address. Only used when creating a new venue (ignored if the venue already exists).
  • organizer — organizer name. Same behaviour as venue (reuses or creates).
  • organizer_email — only used when creating a new organizer.
  • categories — comma-separated list of category names (e.g. "Music,Family,Outdoor"). Categories are matched by name; non-existent ones are skipped (not auto-created — categories control visual colour-coding and should be set up deliberately).
  • featured_image_url — full URL to an image. The importer downloads it, attaches it to the event as the featured image, and stores a copy in your media library.
  • status — publish (default), draft, or private. Useful when preparing a season in advance and publishing later.

Advanced optional columns

  • cost — a free-form price string for free events (e.g. Free, Donations welcome). Doesn't create tickets — see Step 4 below for ticket import.
  • url — an external URL the event title links to instead of the single-event page. Useful for events hosted elsewhere.
  • excerpt — short summary shown in List view tiles when no auto-excerpt is generated.
  • timezone — per-event timezone override (e.g. Europe/London, America/New_York). Only set if this event runs in a different timezone from the site's default.
Compatible with Events Calendar Pro exports. If you're migrating from Modern Tribe's Events Calendar Pro, its CSV export format maps almost 1:1 onto Site Calendar's columns. The header names match for title, start_date, end_date, venue, organizer, categories, and description. You can usually upload an Events Calendar Pro export directly with only minor cleanup (verify date format is YYYY-MM-DD, not MM/DD/YYYY).

Step 3 — Fill in your spreadsheet

Open the downloaded template in your spreadsheet tool. The first row holds the column headers — don't change them. Add one row per event below.

A simple example with three events:

title,start_date,start_time,end_time,venue,categories
Friday night yoga,2026-09-04,18:30,19:30,Community Hall,Wellness
Friday night yoga,2026-09-11,18:30,19:30,Community Hall,Wellness
Friday night yoga,2026-09-18,18:30,19:30,Community Hall,Wellness

Three rows, three identical events on consecutive Fridays. Notice the venue name is repeated — that's fine, the importer reuses the same "Community Hall" venue record across all three.

Repeating events: import or recurrence?

You have a choice when importing a recurring schedule. Either:

  • Import each occurrence as a separate row, as in the example above. Each becomes an independent event you can edit individually. Useful when individual sessions need slightly different details (different teacher, different price, occasional venue change).
  • Import the first occurrence only, then set up recurrence manually in the event editor (see Recurring events). Useful when every occurrence is genuinely identical and you want to manage them as one record.

For 50+ near-identical occurrences, recurrence is cleaner. For 5–20 occurrences with minor variations, importing each row is simpler.

Date formatting — the #1 cause of import errors

Excel and Numbers are notoriously aggressive about reformatting dates. You type 2026-09-14 and the cell silently displays 14/09/2026 — and worse, exports it that way to CSV. The importer rejects rows with malformed dates.

Two fixes:

  1. Format the date column as Text before typing dates. In Excel: select the column → Format Cells → Number → Text → OK. Now Excel won't auto-reformat.
  2. Or use Google Sheets, which respects ISO date strings (2026-09-14) more reliably than Excel when exporting CSV.

After saving the CSV, open it in a plain text editor (Notepad, TextEdit, VS Code) to verify the dates look like 2026-09-14 and not 9/14/26 or 14/09/2026. Five seconds of verification saves a full import retry.


Step 4 — Tickets and recurrence (column extensions)

The base template covers the bulk of what most users need. For more advanced bulk imports, two extension column groups are available:

Ticket columns

  • ticket_1_label, ticket_1_price, ticket_1_capacity — the first ticket type
  • ticket_2_label, ticket_2_price, ticket_2_capacity — the second ticket type
  • ticket_3_label, ticket_3_price, ticket_3_capacity — and so on up to ticket_5

Example: a paid event with adult and child tickets:

title,start_date,start_time,venue,ticket_1_label,ticket_1_price,ticket_1_capacity,ticket_2_label,ticket_2_price,ticket_2_capacity
Summer Fete,2026-08-15,11:00,Recreation Ground,Adult,5.00,200,Child,2.50,200

One row, one event, two tickets — Adult at £5.00 and Child at £2.50, each capped at 200.

Recurrence columns

  • recurrence_frequency — daily, weekly, monthly, or yearly
  • recurrence_interval — the "every N" — e.g. 2 for every 2 weeks
  • recurrence_weekdays — for weekly recurrence, comma-separated weekday names (Monday,Wednesday)
  • recurrence_until — end date in YYYY-MM-DD format, OR
  • recurrence_count — total occurrences (e.g. 10 for a 10-week course)
  • recurrence_skip_dates — comma-separated list of dates to skip (2026-12-25,2027-01-01)

Example: a weekly Tuesday class running for 12 weeks with two skip dates:

title,start_date,start_time,end_time,venue,recurrence_frequency,recurrence_interval,recurrence_weekdays,recurrence_count,recurrence_skip_dates
Beginner Italian,2026-09-08,19:00,20:30,Library,weekly,1,Tuesday,12,"2026-10-27,2026-11-03"

One row, one event, automatic generation of 12 weekly occurrences with two skipped weeks for half-term.

Combining tickets and recurrence. You can use ticket columns and recurrence columns in the same row. The resulting event has the ticket types attached, and the recurrence rule applies the same tickets to every generated occurrence. Capacity is per-occurrence (so a 200-capacity Adult ticket means 200 seats for each Tuesday class, not 200 total across 12 weeks).

Step 5 — Save your CSV and upload

Save your spreadsheet as CSV (Comma-separated values) — not Excel format, not OpenDocument format. The exact menu wording varies:

  • Excel: File → Save As → File Format → CSV UTF-8 (Comma-delimited) (.csv)
  • Numbers: File → Export To → CSV → Save
  • Google Sheets: File → Download → Comma-separated values (.csv)
  • LibreOffice Calc: File → Save As → File type: Text CSV (.csv) → keep current format

Back in WordPress: Site Calendar → Import, drag your CSV file into the drop zone (or click Choose file), then click Import events.

[Screenshot: drag-and-drop area with a CSV file highlighted, and the Import events button enabled]
500 events maximum per upload. For larger imports, split the file by date range.

The importer processes each row in turn, creating events, venues, organizers, and downloading any featured images. Progress shows in real time. A typical 50-event import completes in 5–15 seconds; 500 events take roughly 1–2 minutes depending on whether featured images are included.


Step 6 — Review the import report

When the import completes, you see a row-by-row results table:

[Screenshot: import report table showing each row, status icon (✅ Created / ⚠️ Skipped / ❌ Error), and a brief message column for any errors]
Three statuses: Created (event live), Skipped (duplicate detected), Error (row needs fixing).
  • ✅ Created — the event was created successfully. Click the row to open the new event in the editor.
  • ⚠️ Skipped (duplicate) — an event with the same title and start date already exists. The importer doesn't create duplicates. If you actually wanted to update an existing event, delete the original first and re-import.
  • ❌ Error — the row had a problem. The Message column tells you what (e.g. "Missing required column: start_date", "Invalid date format: 9/14/2026", "Featured image URL not reachable"). Fix the row in your spreadsheet, save a new CSV with just the failed rows, and re-import.

The report is per-import only — it isn't archived. If you need a copy for your records, screenshot it or copy the table content before navigating away.

You're done. Every imported event is now a real WordPress post — editable, deletable, attachable to tickets, indexable by search engines, syndicatable via the ICS feed. The CSV is no longer needed; you can delete it.

Editing imported events

Imported events are standard pcal_event records. To bulk-edit later:

  • Site Calendar → Events — the list view supports WordPress's standard Quick Edit and Bulk Edit (change status, category, etc. on multiple events at once).
  • To re-import a corrected file, first delete the affected events (Site Calendar → Events → select rows → Bulk Actions → Move to Trash). Then re-upload your corrected CSV. The importer's duplicate detection means re-running the same CSV without deleting first will skip everything as duplicates.
  • To migrate to a different site, export with WordPress's standard export tool (Tools → Export → choose Events post type), then import on the new site via Tools → Import → WordPress. The CSV importer is for fresh data; for site-to-site migration, the standard WordPress XML export is the right tool.

Troubleshooting

"All my rows are showing as 'Invalid date format'"

Your spreadsheet has reformatted the dates from YYYY-MM-DD into a locale-specific format (e.g. 14/09/2026 or 9/14/26). Open the CSV in a plain text editor to verify, then either: (1) re-open the spreadsheet, format the date column as Text, retype the dates, re-export; or (2) use Find & Replace in a text editor to convert all dates back to ISO format before re-uploading.

"The importer says 'File too large'"

Two possible limits. (1) The 500-event hard cap — split your file by date range or category and import in batches. (2) Your server's upload_max_filesize setting — usually 2MB or 8MB on shared hosts. A 500-event CSV is well under 100KB, so this is rare unless your CSV contains embedded image data (which it shouldn't — use featured_image_url to reference images by URL instead).

"Special characters (é, ñ, ü, £, €) appear as gibberish in imported events"

Your CSV isn't saved as UTF-8. Excel often defaults to a regional encoding. Re-save with the explicit "CSV UTF-8" option (Excel offers this as a separate format from plain "CSV"). Alternatively, use Google Sheets which always exports UTF-8.

"Imported events are off by one day"

Timezone mismatch. The importer treats dates and times in your site's configured timezone (Settings → General → Timezone). If your site is set to UTC and you've entered 19:00 for a UK summer event, that becomes 18:00 BST when displayed. Fix the site's timezone before importing, or add a per-row timezone column with the appropriate IANA zone (e.g. Europe/London).

"Featured images didn't download"

Three causes. (1) The URL isn't publicly reachable — the importer fetches images server-side, so URLs behind authentication, on localhost, or returning 404 will silently skip. Test each URL in an incognito browser tab. (2) The image file is too large for your server's PHP memory limit — under 5MB per image is safe; large hi-res photos can hit limits. Compress before uploading or host on a CDN that auto-resizes. (3) The image MIME type isn't allowed by WordPress — JPG, PNG, GIF, and WebP work; rarer formats (TIFF, BMP, AVIF) may be blocked depending on site configuration.

"Venues created twice with slightly different names"

The importer matches venues by exact name (case-sensitive). Community Hall and community hall become two separate venues. Same for trailing spaces — "Community Hall " with a trailing space is different from "Community Hall". Standardise the venue names in your CSV before importing (Find & Replace in your text editor is fastest). To merge duplicates after the fact, edit each event to point at the correct canonical venue, then delete the unused duplicate venue record.

"Recurrence columns ignored — only the first occurrence was created"

The recurrence module needs at least recurrence_frequency AND an end condition (either recurrence_until or recurrence_count) to generate occurrences. Missing either, the importer falls back to creating a single non-recurring event. Verify both fields are populated in your CSV.

"Categories from my CSV weren't applied"

The categories column matches by name, and categories aren't auto-created (unlike venues and organizers). Create your categories first via Site Calendar → Events → Categories, exactly matching the names you use in your CSV. Then re-import. The reason auto-creation is disabled here: categories control visual colour-coding, which deserves a deliberate setup rather than incidental creation from a CSV typo.

"How do I undo an import?"

There's no one-click undo. The imported events are standard WordPress posts and can be bulk-deleted via Site Calendar → Events → select all → Bulk Actions → Move to Trash. If you imported very recently and remember the timestamp, sort the events list by date created (or by post ID, since imports get sequential IDs) to find the batch easily. To prevent regret, import a small test batch (5–10 events) first to verify your CSV is correct before running the full file.


What's next

Stuck? Email support@getsitecalendar.com and we'll help directly. Pro licence holders get priority response.