Shopify bulk product upload from CSV with n8n turns a Google Sheet or exported CSV into live Shopify products, one row at a time, with no paid importer app. You map spreadsheet columns to product fields, loop through each row, and POST to the Shopify Admin API on a rate-safe delay. This guide builds the whole flow from scratch and hands you a template you can import in minutes.
If you have ever pasted 200 rows into Shopify’s native CSV importer and watched it silently drop half of them, you know the pain. The built-in importer is rigid about column names, gives you almost no error feedback, and cannot touch anything Shopify did not plan for. This n8n workflow reads your spreadsheet, builds a clean product payload per row, and creates each product through the Admin API so you see exactly what succeeded and what did not.
Prefer to skip the build? Grab the ready-made template and connect it to your store in under ten minutes.
What it does
The workflow reads a list of products from a Google Sheet (or a CSV, with one small swap), then walks through the rows one by one and creates each product in your Shopify store. Here is the flow from the merchant’s point of view:
- You fill a Google Sheet with one product per row, using columns like title, price, SKU and vendor.
- You click Execute in n8n, or wire the workflow to a schedule so it runs on its own.
- n8n reads every row and loops through them at a pace Shopify is happy with.
- Each row becomes a real product in your catalog, complete with a variant, price and SKU.
- You watch the run finish in the n8n execution log, row by row, with a clear pass or fail on each one.
┌──────────────────────────────────────────────────────────────────┐ │ BULK PRODUCT UPLOAD │ │ │ │ [Execute] → [Read rows] → [Loop Over Items] │ │ │ (each row) │ │ ▼ │ │ [Build payload] → [Create product] │ │ ▲ │ │ │ └──── [Wait 0.6s] ◀──┘ │ │ │ (done) │ │ ▼ │ │ [All products created] │ └──────────────────────────────────────────────────────────────────┘
Why it beats the default
Shopify already ships a CSV importer, so why build this? Because the native tool trades control for convenience, and a growing store usually needs the control back.
| Job | Native Shopify CSV import | n8n workflow |
|---|---|---|
| Column naming | Must match Shopify’s exact headers | Map any column name you like |
| Error feedback | Vague, often silent skips | Per-row pass or fail in the run log |
| Extra steps | None possible | Add tagging, alerts or AI copy in the same flow |
| Data source | One CSV upload at a time | Live Google Sheet, CSV, Airtable or an API |
| Cost | Free but limited | Free and extensible |
The real win is that the upload becomes a step you own. Once the rows flow through n8n you can bolt on a Google Gemini node to write descriptions, a tagging step, or a Slack ping when the batch finishes, all without leaving the workflow. For the bigger picture of what else n8n can drive on a store, see the n8n Shopify automation hub.
What you need
- A Shopify store with a custom app created in the 2026 Shopify Dev Dashboard, giving you an Admin API access token with
write_productsscope. - An n8n instance, cloud or self-hosted, on version 1.0 or newer.
- A Google account with a Sheet of products, or a CSV file if you prefer.
- About 30 minutes to build from scratch, or 10 minutes with the template.
If you have not connected Shopify to n8n yet, follow connect Shopify to n8n (2026 method) first. It walks through the Dev Dashboard custom app and the Admin API token this workflow needs. The old admin custom-app screen is gone, so ignore any tutorial that still points there.
The nodes, one by one
The whole flow is seven nodes. Nothing exotic, and no Code node required.
| # | Node | Type | Job |
|---|---|---|---|
| 1 | When clicking Execute | Manual Trigger | Starts the run on demand |
| 2 | Read product rows | Google Sheets (v4.7) | Pulls every row from the sheet |
| 3 | Loop Over Items | Split In Batches (v3) | Feeds one row at a time |
| 4 | Build product payload | Edit Fields / Set (v3.4) | Maps columns to clean typed fields |
| 5 | Create product | HTTP Request (v4.2) | POSTs the product to the Admin API |
| 6 | Wait 0.6s | Wait (v1.1) | Keeps you under the rate limit |
| 7 | All products created | No Operation | Marks the loop as finished |
Build it step by step
1 When clicking Execute (Manual Trigger)
Add a Manual Trigger node. It gives you a button to run the upload on demand while you test. Later you can swap it for a Schedule Trigger if you want the sheet drained automatically every night.
2 Read product rows (Google Sheets)
Add a Google Sheets node, set the operation to Get Row(s), and point it at your document and sheet tab. Connect your Google credential. When it runs, each row comes back as one item. A clean source sheet looks like this:
| Title | Body | Vendor | Type | Price | SKU | Tags | Status |
|---|---|---|---|---|---|---|---|
| Maple Cutting Board | Solid maple, 18 x 12 in. | Northwood | Kitchen | 39.00 | NW-CB-18 | kitchen,wood | active |
| Cast Iron Skillet | Pre-seasoned, 10 in. | Northwood | Kitchen | 29.00 | NW-SK-10 | kitchen,iron | active |
Note: column headers become the field names n8n reads. Keep them simple and consistent, because you will reference them by name in the next node.
3 Loop Over Items (Split In Batches)
Add the Loop Over Items node and set Batch Size to 1. This is the piece that keeps Shopify happy. Instead of firing 200 create calls at once and hitting a rate-limit wall, the loop hands the workflow a single product, waits for it to finish, then serves the next.
Tip: the loop has two outputs. The loop output runs once per row and connects to your build step. The done output fires once at the very end and connects to the final No Operation node.
4 Build product payload (Edit Fields)
Add an Edit Fields (Set) node to translate spreadsheet columns into the exact field names Shopify expects. Create these assignments, each with the matching type:
title (string) = {{ $json.Title }}
body_html (string) = {{ $json.Body }}
vendor (string) = {{ $json.Vendor }}
product_type (string) = {{ $json.Type }}
tags (string) = {{ $json.Tags }}
status (string) = {{ $json.Status }}
price (string) = {{ $json.Price }}
sku (string) = {{ $json.SKU }}
Tip: keep price as a string, not a number. Shopify’s API expects the price as a quoted value like "39.00", and forcing it to a number can drop trailing zeros.
5 Create product (HTTP Request)
Add an HTTP Request node. This is the node that talks to Shopify. Configure it like this:
- Method:
POST - URL:
https://YOUR-STORE.myshopify.com/admin/api/2026-04/products.json - Authentication: Generic Credential Type, then Header Auth. Set the header name to
X-Shopify-Access-Tokenand the value to your Admin API access token. - Send Body: on. Body Content Type: JSON. Specify Body: Using JSON.
- Paste this into the JSON body field as an expression:
{
"product": {
"title": "{{ $json.title }}",
"body_html": "{{ $json.body_html }}",
"vendor": "{{ $json.vendor }}",
"product_type": "{{ $json.product_type }}",
"tags": "{{ $json.tags }}",
"status": "{{ $json.status }}",
"variants": [
{ "price": "{{ $json.price }}", "sku": "{{ $json.sku }}" }
]
}
}
Note: the response returns the new product, including its id and each variant’s inventory_item_id. Keep that in mind if you plan to set stock levels in a later step.
6 Wait 0.6s (Wait)
Add a Wait node set to 0.6 seconds. Shopify’s REST Admin API allows two requests per second on standard plans, so a pause a little over half a second keeps you safely under the ceiling even with retries. Connect the Wait node back into the Loop Over Items node so the loop moves to the next row.
7 All products created (No Operation)
Connect the loop’s done output to a No Operation node named something clear like All products created. It does nothing functional, but it gives the workflow a tidy end point and a spot to add a Slack or Gmail summary later.
Common mistakes
- Leaving Batch Size at its default instead of 1, which fires every row at once and trips Shopify’s rate limit with a wave of 429 errors.
- Wiring the Wait node forward instead of back into the loop, so the workflow processes one product and stops.
- Expecting stock to appear. The create call ignores inventory quantity in current API versions. Stock is set on the inventory_levels endpoint after the product exists.
- Using the old admin custom-app token flow. Create the app in the 2026 Dev Dashboard and use the
X-Shopify-Access-Tokenheader instead. - Sending the body as a raw object or a hand-built string. Use Specify Body: Using JSON so n8n serializes it correctly.
- Re-running on the same sheet and creating duplicates. Add a Status column and an IF node to skip rows already marked Done.
Cost at realistic volume
This is one of those rare automations that costs essentially nothing to run.
| Piece | Cost | Notes |
|---|---|---|
| n8n (self-hosted) | $0 | Runs on your own server, no per-execution fee |
| n8n Cloud | 1 execution per run | A whole batch counts as a single manual execution |
| Shopify Admin API | $0 | Included with any paid Shopify plan |
| Google Sheets | $0 | Free tier is more than enough |
At two requests per second, 500 products upload in roughly five minutes and 5,000 in under an hour. Compared with a paid bulk-import app charging a monthly fee, the running cost here rounds to zero.
Get the ready-to-import template
You now know how every node fits together. If you would rather not wire it by hand, the template drops the whole flow onto your canvas ready to connect.
🚀 Get the Bulk Product Upload template
The guide above is free to follow. If you would rather skip the build, download the ready-to-import workflow, then just add your credentials. Prefer it installed and tuned for you? See our done-for-you services.
Instant download · Works on n8n Cloud and self-hosted
Frequently asked questions
Can I upload products from a CSV file instead of a Google Sheet?
Yes. Drop the CSV in a folder and swap the Google Sheets node for a Read/Write Files node plus an Extract From File node set to CSV. Every other node stays the same because the rows arrive as the same JSON items the loop expects.
How many products can n8n upload to Shopify at once?
There is no hard cap. The workflow loops one row at a time with a short wait, so 500 products take about five minutes and 5,000 take under an hour. The only real limit is Shopify’s two requests per second on the REST Admin API, which the Wait node respects.
Does this set inventory quantity for each product?
The create call sets price, SKU and product details, but stock levels live on a separate inventory endpoint in current Shopify API versions. Add a follow-up HTTP Request to inventory_levels/set once each product returns its inventory_item_id, or set stock by hand for a small catalog.
Will re-running the workflow create duplicate products?
Yes, because products create always makes a new product. To make it safe to re-run, add an IF node that checks a Status column in your sheet and skips rows already marked Done, or search by SKU first and update instead of create.
Do I need a paid n8n plan for this?
No. Self-hosted n8n is free and runs this with no per-execution cost. On n8n Cloud the whole job counts as one execution when triggered manually, so even a large upload stays comfortably inside the Starter plan limits.
Related guides
- Connect Shopify to n8n (2026 method), the token setup this workflow depends on.
- Shopify AI product description generator with n8n, to write the copy before you upload.
- Shopify low stock alert with n8n, to watch inventory once products are live.
- Browse every store automation in the Shopify guides category.