Guide
How to bulk upload products to Shopify
Shopify can create or update many products at once from a CSV file. Here is how the import works, where it gets tricky, and what to do when your product data starts life in supplier PDFs.
Shopify’s product CSV import is the standard way to add a catalog in bulk. You prepare a spreadsheet in Shopify’s format, upload it in the admin, and Shopify creates the products, variants, and images.
As with any import, success depends on the data you put in. This guide covers the CSV process, the alternatives, and the preparation that saves you from cleaning up hundreds of products afterwards.
Option 1: Import a product CSV in the Shopify admin
In the Shopify admin, go to Products and choose Import. Upload a CSV in Shopify’s product format, decide whether products with matching handles should be overwritten, preview, and confirm. Shopify processes the file in the background and notifies you when it is done.
- Start from Shopify’s sample product CSV template, or export an existing product to see the exact columns.
- The URL handle column (Handle in older templates) identifies a product; rows sharing a handle are variants or extra images of the same product.
- Common columns include Title, Description (Body (HTML)), Vendor, Product category, Type, Tags, Status, Option1 name / Option1 value, SKU (Variant SKU), Price, Barcodes (Variant Barcode), and Product image URL (Image Src).
- Images are fetched from public URLs at import time.
- Save the file as UTF-8 CSV to keep special characters intact.
Handling variants correctly
Variants trip up most first imports. Each product can have up to three options (for example Size, Colour, Material). The first row of a product carries the product-level fields and the option names; each following row with the same handle adds one variant with its option values, SKU, price, and inventory.
Keep option names identical across rows and every variant’s combination of option values unique, so each row maps to exactly one variant. A row that has variant columns but no Option1 name and Option1 value makes Shopify create a default variant and delete the existing ones.
Option 2: Bulk import apps
Import apps from the Shopify App Store extend the native CSV: Excel and Google Sheets support, metafields, scheduled imports from supplier feeds, and migration from other platforms. They are worth considering when you import repeatedly or need fields the standard CSV does not cover.
Option 3: The Shopify Admin API
Apps and custom integrations can create products through Shopify’s Admin API instead of a CSV file. This avoids spreadsheet round-trips entirely and is how many catalog and listing tools create products in Shopify.
Doc2Shelf takes this route: you connect your store with OAuth, and reviewed listings are created as draft products with images, prices, and categories.
Preparing product data for Shopify
Before any bulk upload, check that:
- Every product has a unique handle and every variant a unique SKU.
- Titles follow one pattern (for example Brand + Product + Key attribute).
- Descriptions are your own copy, not pasted supplier text, and use simple HTML.
- Specifications use consistent units and names so they can become options, tags, or metafields.
- Products are imported unpublished first so you can review a sample before going live.
When product data comes from supplier PDFs
A CSV import assumes you already have clean, structured data. When suppliers send spec sheets and catalogs as PDFs, someone must first extract titles, specifications, and identifiers, and write descriptions — usually the slowest part of the whole job.
Doc2Shelf replaces that step: it reads supplier PDFs, extracts the product data with confidence information for review, drafts the Shopify listing copy, and creates reviewed products in your connected store as drafts, without a CSV.
Shopify product CSV columns: current names and older equivalents
Shopify has renamed several product CSV columns. Its current CSV reference uses labels such as URL handle, Description, SKU, and Barcodes, while many templates and tutorials still show Handle, Body (HTML), Variant SKU, and Image Src. Shopify says it maintains backward compatibility with older column names, so an older file still imports — but a fresh export uses the current labels, and a file that mixes both styles is hard to check.
The table covers the columns most bulk uploads need. The rename from Barcode to Barcodes is stated in Shopify’s documentation; the other older labels are matched by what each field holds, so confirm them against an export from your own store. Headers are case-sensitive.
| Current column | What it holds | Example | Older label or notes |
|---|---|---|---|
| Title | Product name | Stainless steel kettle 1.7 L | Required to create a product. |
| URL handle | Unique product identifier used in the product URL | steel-kettle | Older templates label it Handle. Rows sharing a handle belong to one product. |
| Description | Product description | Cordless kettle with… | Older templates label it Body (HTML). |
| Vendor | Brand or manufacturer | Acme | — |
| Product category | Standard Product Taxonomy category | hg-15-1-2 | Full breadcrumb or category ID, not both. |
| Type | Your own product type | Electric kettle | Free-form, unlike the category. |
| Tags | Comma-separated tags | cordless, kitchen | Up to 250 tags per product. |
| Status | Product status | draft | Import as draft, review, then activate. |
| SKU | Variant SKU | KTL-SLV | Older templates label it Variant SKU. |
| Barcodes | Barcodes of the variant | gtin:4006381333931 | Formerly Barcode (and Variant Barcode before that). Separate several with semicolons; the type prefix is optional. |
| Option1 name / Option1 value | First option and this variant’s value | Color / Silver | Option2 and Option3 follow the same pattern. |
| Option1 LinkedTo | Link from the option to a category metafield | product.metafields.shopify.[attribute] | Optional; see the taxonomy section below. |
| Price / Compare-at price | Selling price and comparison price | 49.90 / 59.90 | Price / [Market] columns hold market-specific prices. |
| Inventory quantity | Stock on hand | 25 | Works with the Inventory tracker column. |
| Weight value (grams) | Variant weight in grams | 1200 | Older templates label it Variant Grams. Weight unit for display sets the unit shoppers see. |
| Product image URL | Public URL of a product image | https://cdn.example.com/kettle.jpg | Older templates label it Image Src. |
| Image position / Image alt text | Gallery order and alt text | 1 / Steel kettle, front view | Alt text up to 512 characters. |
| Variant image URL | Image shown when this variant is selected | https://cdn.example.com/kettle-black.jpg | — |
| SEO title / SEO description | Title and meta description for search results | — | Up to 70 and 320 characters. |
| Metafield columns | Product metafield values | Material (product.metafields.custom.material) | Header format: Name (product.metafields.namespace.key). |
Variants and options: limits and pitfalls
A Shopify product can have up to three options — for example Size, Color, and Material — and up to 2,048 variants. The variant limit was 100 until Shopify raised it in October 2025, so templates and guides written earlier still quote the old number. Some themes, apps, and sales channels were built around the old limit, so check yours before importing a product with hundreds of variants.
The first row of a product carries the product-level fields and the option names. Each following row with the same URL handle adds one variant with its option value, SKU, price, weight, and inventory, and leaves the product-level fields empty.
Two behaviors catch out update imports. If a row includes variant columns such as SKU or weight but leaves out Option1 name and Option1 value, Shopify creates a default variant and deletes the existing variants. And changing an option value — renaming “Grey” to “Gray”, say — deletes the variant IDs and creates new ones, which can break apps and feeds that reference those IDs.
| Row | URL handle | Title | Option1 name | Option1 value | SKU |
|---|---|---|---|---|---|
| First row | steel-kettle | Steel kettle | Color | Silver | KTL-SLV |
| Next row | steel-kettle | (empty) | (empty) | Black | KTL-BLK |
Product images in a Shopify CSV
Shopify downloads images from the Product image URL column during the import, so each URL must be publicly accessible and point directly at the image file. Links that need a login, open a preview page, or expire before the import runs are the usual reasons images go missing.
A product can hold up to 250 images or other media. Put the first image on the product’s first row; add each further image on its own row with the same URL handle, the image URL, its Image position, and its Image alt text. To show a particular image when a shopper picks a variant, put its URL in Variant image URL on that variant’s row.
Alt text can be up to 512 characters, and Shopify recommends 125 or fewer. Describe what the image shows — “Stainless steel kettle, front view with water gauge” — rather than repeating the product title.
Product category, metafields, and the Standard Product Taxonomy
The Product category column assigns a category from Shopify’s Standard Product Taxonomy, chosen by the product’s main function. Enter either the full breadcrumb, such as Home & Garden > Linens & Bedding > Bedding > Bed Sheets, or the category ID, such as hg-15-1-2 — not both. A product without a category is treated as uncategorized.
Setting a category unlocks its category metafields, which the taxonomy calls product attributes. Shopify’s Shirts category, for example, has nine, including size, neckline, fabric, and color, and color values can display as swatches. The Option1 LinkedTo column connects a variant option to one of these category metafields.
The category matters beyond navigation: Shopify Tax uses it to set tax rates. Type, by contrast, is a free-form label you define yourself, useful for your own filters and reports.
Custom product metafields import through columns headed Name (product.metafields.namespace.key), for example Material (product.metafields.custom.material). Variant metafields are not supported in the product CSV, so set those in the admin, with an app, or through the API.
Shopify CSV limits and SEO field lengths
These are the limits Shopify documents for product CSV imports and product records. Numbers in older guides — especially the 100-variant limit — are out of date.
The SEO title and SEO description columns fill the title and meta description used in search results. Write them per product instead of copying one pattern across the catalog: lead with the product name and the attribute shoppers search for, such as size, material, or model number.
| Item | Limit | What to do |
|---|---|---|
| CSV file size | 15 MB | Split large catalogs; keep all rows of a product in one file. |
| Options per product | 3 | Model further differences as separate products or metafields. |
| Variants per product | 2,048 | Check that your theme and apps handle large variant counts. |
| Images and media per product | 250 | Keep the gallery to images that help shoppers decide. |
| Tags per product | 250 | Use tags for filtering and automation. |
| Barcodes per product or variant | 20 | Add a type prefix such as gtin: where you know the type. |
| SEO title | 70 characters | Lead with the product name and key attribute. |
| SEO description | 320 characters | Summarize the product with key details first. |
| Image alt text | 512 characters (125 or fewer recommended) | Describe what the image shows. |
Overwrite or create: how updates behave
To create a product, the CSV needs only a Title; adding variants also needs the URL handle. To update an existing product, Shopify needs the URL handle and Title, and you choose to overwrite products with matching handles when you start the import.
Treat an overwrite as authoritative for every column you include. The safest pattern is export, edit, re-import: export the products you want to change, edit only the cells that need changing, keep the option columns in place, and import the edited file with overwrite turned on. A sparse file built from scratch makes it easy to trigger the variant-deletion behavior described above.
Read the import preview before you confirm, and run a large update against a handful of products first. The export you made before editing is your way back if something goes wrong.
Pre-import checklist and troubleshooting
Work through these steps before a large import; the table after them covers the problems that come up most often.
- Export one product from your store and reuse its header row, so the column names match exactly.
- Give every product a unique URL handle and every variant a unique SKU.
- Check option names and values for consistent spelling, and confirm every variant row has its option value.
- Fill Product category with a breadcrumb or an ID copied from the taxonomy.
- Open a sample of image URLs in a private browser window to confirm they load without a login.
- Set Status to draft for the first run.
- Save as UTF-8 CSV and confirm the file is under 15 MB.
- Import five to ten products, review them in the admin and on the storefront, then import the rest.
| Problem | Likely cause | Fix |
|---|---|---|
| File rejected | Larger than 15 MB, or not saved as UTF-8 CSV | Split the file by product and re-save as UTF-8 CSV |
| Column ignored | Header misspelled or capitalized differently | Copy headers from a fresh export |
| Variants gone after an update | Variant columns sent without Option1 name and value | Re-import with the option columns included |
| Apps lost track of variants | An option value was renamed, creating new variant IDs | Rename option values deliberately and reconnect affected apps |
| Products split or merged wrongly | URL handle differs across one product’s rows | Use one identical handle per product |
| Images missing | URL not public, not a direct file link, or expired | Host images at stable public URLs |
| Category not set | Breadcrumb mistyped, or both breadcrumb and ID given | Use exactly one, copied from the taxonomy |
| Barcode not accepted | Length does not fit the stated type | GTIN: 8, 12, 13, or 14 digits; UPC: 8 or 12; EAN: 8 or 13 |
| Metafield values missing | Wrong header format, or a variant metafield | Use Name (product.metafields.namespace.key); set variant metafields elsewhere |
Frequently asked questions
- How do I bulk upload products to Shopify for free?
- Use the built-in CSV import under Products → Import. It is included with every Shopify plan and handles products, variants, and images from a CSV file in Shopify’s format.
- Can I upload products to Shopify from Excel?
- The native importer needs CSV, so save your Excel sheet as a UTF-8 CSV with Shopify’s column names. Some import apps can read Excel files directly.
- Will a CSV import overwrite my existing products?
- Existing products are only updated when you choose the option to overwrite products with matching handles. Review the import preview before confirming, and test with a small file first.
- Why are my images not importing?
- Image URLs must be publicly accessible direct links to image files. Links that need a login, redirect to a web page, or come from a temporary file share usually fail.
- Which columns are required in a Shopify product CSV?
- Only Title is required to create a product, plus URL handle when you add variants. To update existing products, include URL handle and Title. Everything else is optional, although a usable listing normally needs a price, SKU, category, description, and images.
- How many variants can a Shopify product have?
- Up to 2,048 variants across a maximum of three options. Shopify raised the limit from 100 in October 2025, so older templates and guides that quote 100 are out of date.
- Can I import metafields with a Shopify CSV?
- Product metafields, yes: add a column headed Name (product.metafields.namespace.key). Variant metafields are not supported in the product CSV and need to be set in the admin, with an app, or through the API.
- What is the maximum size of a Shopify product CSV?
- A product CSV can’t exceed 15 MB. Split larger catalogs into several files and keep all the rows for one product in the same file.