Importing Blog Posts
The Blog Posts import creates and updates the articles inside your store's blogs from a CSV/Excel file, a Google Sheet, or directly from another connected store. Each post belongs to a blog, so every row names its blog handle and its own post handle — that pair is the identity, so running the same file twice updates the same posts instead of creating duplicates.
To manage the blogs themselves (the containers), see Importing Blogs. For the wizard itself — uploading, mapping, dry runs, schedules — see Importing into EcomSolo.
Import the blog first
A post can't exist without its blog. Before importing posts, make sure each Blog handle in your file already exists on the target store — import the Blogs first (or let a sync bring them in). A row whose blog isn't found is reported with a clear message so you can fix it and re-run.
Columns
| Column | Notes |
|---|---|
| Blog handle | The handle of the parent blog (e.g. news). Required — says which blog the post lives in. |
| Post handle | The post's own handle (e.g. summer-sale). Required — unique within its blog; the two handles together match and create posts. |
| Title | The post title. Required by Shopify when creating a new post. |
| Author | The author's name. Required by Shopify when creating a new post. |
| Body (HTML) | The post content, HTML allowed. |
| Summary / excerpt (HTML) | Short summary shown in blog listings. |
| Tags | Comma-separated list of tags. |
| Published | true publishes the post; false keeps it a draft. Blank leaves the current state. |
| Published at | Publish date/time (ISO 8601, e.g. 2026-06-01). |
| Image URL | A public URL to the hero image — Shopify downloads and hosts a copy. |
| Image alt text | Alt text for the hero image. |
| Template suffix | Theme template suffix; blank uses the default. |
Common export headers map automatically: Blog Handle → Blog handle,
Article Handle / Post Handle → Post handle, Body (HTML) → Body,
Excerpt → Summary, Image Src → Image URL, Published At → Published at.
How posts are matched
Every row is matched to an existing post by blog handle + post handle in the target store. What happens on a match depends on the operation you pick:
| Operation | On an existing post | On a new pair |
|---|---|---|
| Upsert (default) | Updates it | Creates it (Title + Author required) |
| Create | Fails the row | Creates it (Title + Author required) |
| Update | Updates it | Fails the row |
| Merge | Updates it | Fails the row |
| Delete | Deletes it | Skips the row |
The dry run shows the create / update / delete split before anything is written.
Deleting posts
Choose the Delete operation to remove posts. A delete file needs only the identity columns — Blog handle and Post handle — of each post to remove; content columns are ignored. Deletes are permission-gated (content write access) and irreversible, so the dry run always shows the count first. A post that no longer exists is quietly skipped, so re-running the same delete file is safe (idempotent).
First-time note: matching works through EcomSolo's synced copy of your posts. If a post already exists on Shopify but was never synced into EcomSolo, the first create can collide on the handle — run a blog sync (or import the blog's posts once) so EcomSolo knows about it, then re-runs update cleanly.
Importing from another store
On the wizard's Source step, pick From another store to copy posts from a connected store — title, author, body, tags, hero image, and publish state come across, matched by blog + post handle. Import the Blogs first so each post's parent blog exists on the target. Combined with a schedule this becomes a recurring one-way blog-post sync.
Results and freshness
Posts you create or update take effect in Shopify immediately, and EcomSolo's Blog posts dashboard refreshes within seconds of the run.
Sample file
A ready-to-run example ships as blog-posts-sample.csv:
Blog Handle,Post Handle,Title,Author,Body (HTML),Tags,Published,Published At,Image URL
wow-news,welcome,Welcome to Wow News,Ada Wow,<p>Our very first post.</p>,announcement,true,2026-01-02,https://picsum.photos/seed/wownews1/1200/630.jpg
wow-news,summer-sale,Summer Sale Is Live,Ada Wow,<p>Big wow discounts inside.</p>,"sale, summer",true,2026-06-01,https://picsum.photos/seed/wownews2/1200/630.jpg
wow-guides,sizing,How to Pick Your Size,Sam Wow,<p>A quick sizing guide.</p>,guide,false,,
This adds two posts to the wow-news blog and one draft to wow-guides
(create those blogs first with the Blogs sample).
Troubleshooting
| Error in the report | What it means / what to do |
|---|---|
row requires a Blog handle | Every row must name the parent blog's handle. |
row requires a Handle | Every row needs the post's own handle. |
blog with handle '…' was not found | The parent blog doesn't exist on the target — import Blogs first, then re-run. |
row would create a new post but has no Title | Shopify requires a title to create a post. |
row would create a new post but has no Author | Shopify requires an author to create a post. |
… access scope / missing permission | The store isn't connected with content write access — reconnect it from its settings, then re-run. |
FAQ
Will re-importing create duplicates? No — rows match by blog + post handle, and the default Upsert operation updates in place.
Can I move a post to a different blog? Yes — change its Blog handle to the target blog (which must exist) and re-run; the post moves on update.
Does it import comments? No — comments are out of scope for the importer.
Can I delete posts? Yes — run the file with the Delete operation and the blog + post handle of each post. It's permission-gated and irreversible, and missing posts are skipped. See Deleting posts.