Google Sheets to Shopify, clean product imports
You build a neat product sheet, hit “import,” and then the cleanup starts. Variants don’t line up, SKUs get messy, inventory is wrong, and images fail silently. Now you’re stuck doing manual fixes inside Shopify, one product at a time.
This Shopify import automation hits e-commerce managers hardest during launches, but agency implementers and operators running lean stores feel it too. You will turn Google Sheets rows into correctly structured Shopify products (variants included) without the usual rework.
This workflow reads your sheet, groups variants by SKU patterns, creates products through Shopify’s GraphQL API, sets inventory at your default location, and attaches images from URLs. Here’s what it looks like in practice and how to make it yours.
How This Automation Works
See how this solves the problem:
n8n Workflow Template: Google Sheets to Shopify, clean product imports
flowchart LR
subgraph sg0["When clicking ‘Execute workflow’ Flow"]
direction LR
n0@{ icon: "mdi:play-circle", form: "rounded", label: "When clicking ‘Execute workf..", pos: "b", h: 48 }
n1@{ icon: "mdi:database", form: "rounded", label: "Get row(s) in sheet", pos: "b", h: 48 }
n2["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/graphql.png' width='40' height='40' /></div><br/>Shopify, GetLocations"]
n3["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/graphql.png' width='40' height='40' /></div><br/>Shopify, CreateProduct"]
n4@{ icon: "mdi:swap-vertical", form: "rounded", label: "Split Out1", pos: "b", h: 48 }
n5["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/graphql.png' width='40' height='40' /></div><br/>SetVariant"]
n6["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/graphql.png' width='40' height='40' /></div><br/>Update Variants"]
n7["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/graphql.png' width='40' height='40' /></div><br/>SetInventory"]
n8["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/graphql.png' width='40' height='40' /></div><br/>SetVariant1"]
n9["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/graphql.png' width='40' height='40' /></div><br/>CreateProduct2"]
n10["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/graphql.png' width='40' height='40' /></div><br/>Create SetInventory"]
n11["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/code.svg' width='40' height='40' /></div><br/>single and multivariant prod.."]
n12@{ icon: "mdi:swap-horizontal", form: "rounded", label: "is variant?", pos: "b", h: 48 }
n13@{ icon: "mdi:swap-vertical", form: "rounded", label: "set shop url", pos: "b", h: 48 }
n14@{ icon: "mdi:swap-vertical", form: "rounded", label: "set variants data", pos: "b", h: 48 }
n15["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/code.svg' width='40' height='40' /></div><br/>adjust variants"]
n5 --> n14
n4 --> n5
n8 --> n10
n12 --> n3
n12 --> n9
n13 --> n2
n9 --> n8
n6 --> n7
n15 --> n4
n14 --> n6
n1 --> n11
n2 --> n1
n3 --> n15
n11 --> n12
n0 --> n13
end
%% Styling
classDef trigger fill:#e8f5e9,stroke:#388e3c,stroke-width:2px
classDef ai fill:#e3f2fd,stroke:#1976d2,stroke-width:2px
classDef aiModel fill:#e8eaf6,stroke:#3f51b5,stroke-width:2px
classDef decision fill:#fff8e1,stroke:#f9a825,stroke-width:2px
classDef database fill:#fce4ec,stroke:#c2185b,stroke-width:2px
classDef api fill:#fff3e0,stroke:#e65100,stroke-width:2px
classDef code fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
classDef disabled stroke-dasharray: 5 5,opacity: 0.5
class n0 trigger
class n12 decision
class n1 database
class n11,n15 code
classDef customIcon fill:none,stroke:none
class n2,n3,n5,n6,n7,n8,n9,n10,n11,n15 customIcon
The Challenge: Spreadsheet Imports That Break on Variants
Spreadsheets are great for collecting product info, but they’re terrible at enforcing structure. One person types “SM,” another types “Small,” and suddenly your “variants” are just separate products. Add inventory to the mix and it gets worse: Shopify wants inventory tied to a specific location and variant ID, not a vague “On hand” number in a cell. Then images show up as yet another fire drill, because a URL that loads in a browser can still fail at import time. Frankly, the worst part is you don’t notice the mistakes until customers do.
It adds up fast. Here’s where it breaks down.
- You end up manually creating options (size, color) because the import can’t reliably group rows into one product.
- SKUs drift over time, which means reporting and fulfillment rules stop matching reality.
- Inventory gets set incorrectly because the update needs the right Shopify location and variant mapping.
- Image uploads fail or attach to the wrong item, so someone has to check every product page.
The Fix: Automated Google Sheets → Shopify Product Creation
This workflow turns your Google Sheet into a reliable source of truth for Shopify product creation. You run it, it fetches your Shopify inventory location, then pulls rows from your sheet and groups them into products. If a product is simple (one row, no variants), it follows a clean “single item” path and creates the product, sets the SKU, then sets inventory at your default location. If the product has multiple rows, it detects variants by parsing your SKU format (like BASESKU-SM, BASESKU-MD), creates the parent product with options, and then updates each variant with the correct SKU, price, stock level, and image. The outcome is boring in the best way: the catalog appears in Shopify the way you expected, without a long QA checklist.
The workflow starts with a manual run trigger, so you control when imports happen. Then it retrieves locations from Shopify and reads sheet rows from Google Sheets. After a grouping step, it branches: multi-variant products go through a batch create and per-variant updates, while single products take a shorter path that still sets SKU and inventory correctly.
What Changes: Before vs. After
| What This Eliminates | Impact You’ll See |
|---|---|
|
|
Real-World Impact
Say you’re launching 40 products, and 25 of them have variants. Manually, even “quick” entry is maybe 6 minutes per simple product (about 1 hour total) plus around 3 minutes per variant row for setup and corrections (often another 2 hours), and that’s before images and inventory checks. With this workflow, you spend about 20 minutes cleaning the sheet and then run the import; Shopify creation and inventory updates happen automatically while you do something else. For many stores, that’s roughly 2 to 3 hours back on launch day, and fewer “why is this out of stock?” tickets the next morning.
Requirements
- n8n instance (try n8n Cloud free)
- Self-hosting option if you prefer (Hostinger works well)
- Google Sheets to store product rows and images.
- Shopify Admin API (GraphQL) to create products, variants, and inventory.
- Shopify Admin API token (get it from Shopify Admin → Apps → Develop apps).
Skill level: Intermediate. You’ll connect accounts, paste an API token, and confirm your sheet columns match the expected format.
Need help implementing this? Talk to an automation expert (free 15-minute consultation).
The Workflow Flow
You run the workflow on demand. It starts with a manual trigger so you can import when the sheet is ready, not every time someone edits a cell.
Shopify location is fetched first. The workflow calls Shopify GraphQL to find your inventory location, because inventory updates need a real location ID to be accurate.
Sheet rows are pulled and grouped into products. It reads Google Sheets, then processes rows to detect which products are single items and which should become multi-variant listings. SKU patterns (like BASESKU-SM) are used to group variants together.
Products, variants, and inventory are created in the right order. Variant products go through a “create parent product” action, then each variant gets updated with SKU, pricing, and inventory. Single products take a shorter create-and-update path, but still end with inventory set correctly.
Results land in Shopify ready to merchandize. Your catalog shows up with the expected options and stock levels, and product images are attached from the URLs provided in the sheet.
You can easily modify vendor defaults to match your store’s naming conventions based on your needs. See the full implementation guide below for customization options.
Step-by-Step Implementation Guide
Step 1: Configure the Manual Trigger
Start the workflow with a manual trigger and define your Shopify GraphQL endpoint in a set node.
- Add Manual Run Trigger as the starting node.
- Open Assign Shop URL and set myshop to
https://[yourshop].myshopify.com/admin/api/2025-04/graphql.json. - Keep Flowpast Branding as a non-functional reference note (optional, no configuration needed).
- Connect Manual Run Trigger → Assign Shop URL.
[yourshop] with your real Shopify subdomain or the GraphQL requests will fail.Step 2: Connect Google Sheets
Pull product rows from your spreadsheet to drive the import.
- Add Fetch Shopify Locations after Assign Shop URL.
- In Fetch Shopify Locations, set Endpoint to
={{ $('Assign Shop URL').item.json.myshop }}and keep Authentication asheaderAuth. - Credential Required: Connect your
httpHeaderAuthcredentials for Fetch Shopify Locations. - Add Retrieve Sheet Rows and set Document to
Shopify Store Detailsand Sheet toProducts. - Credential Required: Connect your
googleSheetsOAuth2Apicredentials for Retrieve Sheet Rows. - Connect Fetch Shopify Locations → Retrieve Sheet Rows.
Product Name, SKU, Size, Price, On hand Inventory, and Product Image.Step 3: Set Up Processing Logic
Transform your sheet rows into structured objects for single or variant products.
- Add Process Product Variants and paste the provided JavaScript exactly as-is to group products and build variant metadata.
- Connect Retrieve Sheet Rows → Process Product Variants.
- Add Branch by Variant Type to route items based on the
typefield. - In Branch by Variant Type, ensure the rule uses
={{ $json.type }}equalsvariantfor the Variant output and={{ $json.type }}equalssinglefor the Single output. - Connect Process Product Variants → Branch by Variant Type.
Step 4: Configure Shopify Product Creation
Create products in Shopify for variant and single items, then prepare variant payloads for bulk creation.
- Connect the Variant output of Branch by Variant Type to Create Shopify Item.
- In Create Shopify Item, set Endpoint to
={{ $('Assign Shop URL').first().json.myshop }}and keep the provided GraphQL Query and Variables (including{{ $json.productName }},{{ $json.productImage }}, and{{JSON.stringify($json.optionsGraph)}}). - Credential Required: Connect your
httpHeaderAuthcredentials for Create Shopify Item (this same credential is reused across all Shopify GraphQL nodes). - Connect Create Shopify Item → Prepare Variant Payloads.
- Add Prepare Variant Payloads and keep the provided JavaScript so it maps option values by size using Branch by Variant Type data.
- Connect the Single output of Branch by Variant Type to Create Single Item, with Endpoint set to
={{ $('Assign Shop URL').first().json.myshop }}and the provided GraphQL variables (including{{ $json.productName.replaceAll(" ", "-") }}). - Credential Required: Connect your
httpHeaderAuthcredentials for Create Single Item.
httpHeaderAuth credential—apply it consistently to Fetch Shopify Locations, Create Shopify Item, Create Variant Batch, Update Variant Details, Adjust Inventory Levels, Create Single Item, Update Single Variant, and Set Single Inventory.Step 5: Configure Variant Batching, Updates, and Inventory
Split variants, create them in bulk, and then update price and inventory for both variant and single products.
- Add Split Variant Items and set Field to Split Out to
variantsand Fields to Include toproductId, productTitle, mainfirstVariant, mediaID. - Connect Prepare Variant Payloads → Split Variant Items → Create Variant Batch.
- In Create Variant Batch, set Endpoint to
={{ $('Assign Shop URL').first().json.myshop }}and keep the variables mapping including{{ $json.productId }},{{ $json.variants.price }}, and{{ $json.variants.optionValues[0].optionId }}. - Credential Required: Connect your
httpHeaderAuthcredentials for Create Variant Batch. - Connect Create Variant Batch → Map Variant Fields and keep the assignments, especially
={{ $('Split Variant Items').item.json.variants.inventoryItem.inventory }}and the fallback ID mapping fordata.productVariantsBulkCreate.productVariants[0].id. - Connect Map Variant Fields → Update Variant Details → Adjust Inventory Levels and keep all GraphQL variables using
={{ $('Assign Shop URL').first().json.myshop }}and the location ID from={{ $('Fetch Shopify Locations').first().json.data.locations.edges[0].node.id }}. - Connect Create Single Item → Update Single Variant → Set Single Inventory and keep the inventory set input (including
={{ $('Branch by Variant Type').item.json.inventory }}).
Step 6: Test and Activate Your Workflow
Validate the full flow end-to-end and then enable it for ongoing use.
- Click Execute Workflow and confirm Manual Run Trigger starts the execution.
- Check the output of Retrieve Sheet Rows to ensure product data is loaded from
Shopify Store Details→Products. - Verify the switch routing in Branch by Variant Type sends items to Create Shopify Item or Create Single Item appropriately.
- Confirm Shopify responses include product IDs and variant IDs in Create Shopify Item, Create Variant Batch, and Create Single Item.
- Once successful, toggle the workflow Active to use it in production.
Watch Out For
- Shopify Admin API credentials can expire or lack scopes. If things break, check your app’s API scopes and token status in Shopify Admin first.
- If you’re using batch processing (Split in Batches) and Shopify is busy, processing times vary. Bump up any waiting or reduce batch size if downstream GraphQL calls fail or come back incomplete.
- Google Sheets data quality matters more than you think. If your SKU format isn’t consistent (BASESKU-VARIANT), the grouping logic will split products incorrectly and you’ll chase “missing variants” afterward.
Common Questions
About 15–30 minutes if your Shopify token and sheet columns are ready.
Yes. No coding is required, but someone does need to follow the sheet format and paste the Shopify Admin API token into n8n.
Yes. n8n has a free self-hosted option and a free trial on n8n Cloud. Cloud plans start at $20/month for higher volume. You’ll also need to factor in Shopify API usage (usually negligible for normal imports).
Two options: n8n Cloud (managed, easiest setup) or self-hosting on a VPS. For self-hosting, Hostinger VPS is affordable and handles n8n well. Self-hosting gives you unlimited executions but requires basic server management.
You can. Most changes happen in the nodes that assign your store URL and map fields before the GraphQL create/update calls. Common tweaks include defaulting vendor and product type, switching product status to draft for safer QA, adding a description field from the sheet, or extending variant options beyond size (like color or material).
Usually it’s an invalid or expired Admin API token, or the app doesn’t have the right scopes for products and inventory. Double-check the store URL you set in the workflow, because a missing “myshopify.com” domain causes confusing GraphQL errors. If it fails only on larger imports, you may be hitting rate limits; reduce batch size and try again. Also confirm your inventory location exists and is returned by the “fetch locations” call, otherwise inventory updates will fail even if product creation succeeds.
It’s built to handle up to 100 variants per product, and it processes products in batches so you can import a full catalog in a few runs. On n8n Cloud Starter you’re limited by monthly executions, while self-hosting has no execution cap (your server is the limit). In practice, most stores can import hundreds of rows comfortably as long as the sheet is clean and your Shopify API token has the right permissions.
Often, yes, because variant creation plus inventory setting usually needs branching logic, batching, and a few “glue” steps that get awkward (and expensive) in Zapier or Make. n8n also gives you the option to self-host for unlimited runs, which matters when you’re iterating on a catalog import. The tradeoff is setup: you’ll spend a bit more time getting it right the first day. If you only ever import a handful of simple products, a lightweight tool might be fine. If you’re not sure, Talk to an automation expert and we’ll sanity-check your use case.
Once this is running, product imports stop being a risky project and start being a routine. Set it up, keep your sheet clean, and let Shopify fill itself in.
Need Help Setting This Up?
Our automation experts can build and customize this workflow for your specific needs. Free 15-minute consultation—no commitment required.