Reference
The Shopify product CSV, column by column
What each column expects, the limits that are enforced, and a decoder for the import failures merchants actually hit. Written from building the validator — every rule below is one we check.
Almost every confusing Shopify import traces back to one idea that the format never states out loud: a row is not a product. The Handle column is the grouping key, and every consecutive row sharing a handle belongs to the same product — additional rows carry additional variants and additional images, with most other columns deliberately left blank.
Once that clicks, the two classic disasters explain themselves. Rows that were meant to be separate products get folded into one product because they happened to share a handle. And a spreadsheet "tidied up" by filling every blank cell turns continuation rows into contradictory instructions. Blank is meaningful here.
Hard limits
What is enforced, not advisory
- Product CSV uploads must be 15 MB or smaller.
- The file must be UTF-8. A UTF-8 BOM in front of the first header breaks that header.
- Header names are matched exactly — casing and stray whitespace both matter.
- Handles use lowercase letters, numbers, and hyphens.
- Variant Weight Unit must be one of g, kg, lb, or oz.
- Variant Inventory Policy must be deny or continue.
- Image URLs are fetched by Shopify at import time, so they must be publicly reachable.
Column reference
What each column expects
Identity — the columns that decide what becomes a product
These four do most of the damage when they are wrong, because they control how rows are grouped into products rather than what a product says.
- Handle
- Lowercase letters, numbers, and hyphens. This is the grouping key: every row sharing a handle becomes one product. It is also the product's URL slug.
- Title
- Required on the first row of each handle. Continuation rows (extra variants, extra images) leave it blank on purpose.
- Status
- active, draft, or archived.
- Published
- TRUE or FALSE — whether the product shows on the online store channel.
Product content
Set on the first row of a handle; blank on continuation rows.
- Body (HTML)
- HTML. Commas and quotes are fine as long as the field is quoted correctly.
- Vendor
- Free text.
- Type
- Free text — your own product type, not the Shopify taxonomy.
- Product Category
- A Shopify taxonomy breadcrumb with spaces around the separator: Apparel & Accessories > Clothing. Not a URL, and not an HTML-encoded &.
- Tags
- Comma-separated inside one quoted cell.
Variants
Option columns come in name/value pairs. A name without its value is one of the most common hard failures.
- Option1 Name / Option1 Value
- Both or neither. Option1 Name is set once per product; every variant row carries its own Option1 Value.
- Option2 / Option3
- Same pairing rule, only used if the product has that many options.
- Variant SKU
- Your identifier. Shopify does not require uniqueness — but your ops almost certainly do.
- Variant Barcode
- GTIN-8/12/13/14 in practice. The check digit is verifiable before upload.
Inventory, weight, and shipping
The field names here are the ones spreadsheets most often mangle.
- Variant Inventory Tracker
- shopify, or blank for untracked.
- Variant Inventory Policy
- deny or continue — whether to keep selling at zero stock.
- Variant Fulfillment Service
- manual, or the handle of a fulfillment service.
- Variant Grams
- A whole number of grams. This is the actual weight field — a column called “Variant Weight” is not the Shopify header and will not map.
- Variant Weight Unit
- g, kg, lb, or oz. Nothing else, and no “lbs”.
- Variant Requires Shipping
- TRUE or FALSE.
- Variant Taxable
- TRUE or FALSE.
Pricing
Numeric only. Currency symbols and thousands separators are a spreadsheet's doing, not yours.
- Variant Price
- A plain number: 19.99. No $, no €, no thousands separator.
- Variant Compare At Price
- Same rules. Leave blank rather than writing 0 unless you mean it.
- Cost per item
- Plain number. Feeds margin reporting, not the storefront.
Images
Image rows are the clearest example of the continuation-row model: extra images are extra rows under the same handle.
- Image Src
- A publicly reachable URL. Shopify fetches it server-side during import, so anything behind a login, a signed expiry, or a local path silently produces a product with no image.
- Image Position
- 1, 2, 3… Position 1 is the featured image.
- Image Alt Text
- Free text.
- Variant Image
- A URL that must also appear as an Image Src somewhere in the same product.
Error decoder
Symptom → what is actually wrong
- The import runs but creates far fewer products than rows
- Cause: Rows that were meant to be separate products share a handle, so Shopify folded them into one product as variants.
Fix: Check for duplicate handles before uploading — and remember that two different titles can normalise to the same handle. - “Invalid header” or columns silently ignored
- Cause: Header names are matched exactly. Trailing spaces, different casing, or a friendly alias like “Price” instead of “Variant Price” all break the mapping.
Fix: Trim the header row and map aliases back to canonical Shopify names. - The very first column name looks right but is rejected
- Cause: A UTF-8 BOM is sitting in front of it. Excel writes one routinely, and it makes “Handle” read as “Handle”.
Fix: Strip the BOM. This is a safe, unambiguous fix. - Accented characters and symbols arrive as ? or �
- Cause: The file was saved in a local code page rather than UTF-8. Excel does this by default on many systems.
Fix: Re-save as CSV UTF-8. Once the characters are replaced there is nothing left to recover — fix at the source. - The upload is rejected before any row is read
- Cause: Shopify's product CSV import limit is 15 MB.
Fix: Split the file, keeping every row of a handle together in the same part. - Products import with no image
- Cause: Shopify could not fetch the Image Src URL. Import does not fail for this — you get the product without the picture.
Fix: Check that every image URL is publicly reachable before uploading, not after. - Variants collapse or come in wrong
- Cause: Option1 Name is set without an Option1 Value on a row, or option columns are used out of order.
Fix: Keep name/value paired, and do not use Option2 without Option1. - Weights are all zero or ignored
- Cause: The weight lives in a column called “Variant Weight”, which is not a Shopify header, or Variant Weight Unit says “lbs”.
Fix: Use Variant Grams with a whole number, and one of g / kg / lb / oz. - An update file overwrote fields you did not intend to change
- Cause: A blank cell in a product CSV is an instruction, not an omission — the importer writes the blank.
Fix: Before uploading an update, review which columns are present and blank. This is the single most expensive Shopify CSV mistake. - Variant metafields do not appear after import
- Cause: Native product CSV import does not reliably set variant-level metafields, regardless of how the columns are named.
Fix: Use a dedicated metafield import path. No CSV column arrangement fixes this one.
Honest limits
What no CSV tool can do for you
- Decide whether two rows with the same handle are a mistake or a deliberate variant grouping. Only you know the catalog.
- Recover characters already destroyed by a bad encoding save. Once text is replacement characters, the original bytes are gone — fix it at the export step.
- Tell whether
1,50means one-fifty or one thousand five hundred. Comma decimals stay a warning here rather than an automatic "fix", because guessing wrong changes your prices. - Set variant metafields through the native product CSV import. That path does not work reliably.
- Replace Shopify's importer. This validates the file before you upload it in Shopify admin.
Next
Check a file against all of this
The CSV scanner runs these rules on your own file in the browser — nothing is uploaded. Related checks: duplicate handles, header names, encoding, image reachability, and update-file blank-cell risk.