Import products from CSV
Bring an existing catalog in as one file — with the exact column spec, how updates are matched, and what the console rejects before writing anything.
Adding products one at a time is fine for ten. For a hundred, put them in a CSV and import it. Coming from Shopify? Import its export directly — see Move a Shopify catalog to CordeliaApps. The importer validates the entire file before writing anything, so a row with a bad price or an unknown brand costs you a re-upload rather than a half-imported catalog.
Open Products → Import CSV in the console.
The dialog has a Download sample button. It produces a valid file with your store's own categories and brands already in it — start from that rather than from a blank spreadsheet.
Prepare your categories and brands first
A product row references categories and brands by name, and the importer will not invent one that doesn't exist. Create them before importing, or every row referencing a missing name fails validation.
Categories and brands have CSV importers of their own, so a full catalog migration is three files in order: categories, brands, then products.
The columns
| Column | Required | What it holds |
|---|---|---|
name | Yes | Product name. Also the match key when id is blank. |
price | Yes | What the shopper pays, in the store's currency. Must be above 0. |
original_price | No | Pre-discount price. Blank means not discounted. |
unit_value | No | Pack size as a number, e.g. 500. Leave blank for non-packaged goods. |
unit_type | No | One of g, ml, pcs. Blank means g. |
stock | Yes | A whole number, 0 or more. |
description | No | Shown on the product's details screen. |
prep_time | No | Free text, e.g. 10 mins. |
image_url | No | Public https URL of the product photo. |
categories | No | Existing category names, separated by |. |
brand | No | An existing brand name. Blank means unbranded. |
is_popular | No | true/false — feeds the storefront's popular rail. |
sku | No | Your stock-keeping code. |
barcode | No | EAN / UPC / ISBN. |
external_id | No | The id another system knows this product by (a Shopify handle, an ERP code). The match key before name — see below. |
id | No | An existing product id. Present means update that exact product. |
option1_name … option3_name | No | For a product that comes in choices (Size, Colour): the option names. Only on the product's first row. |
option1_value … option3_value | No | This row's value for each option (M, Red). One row per variant, all sharing the product's name or external_id; price, stock, sku and barcode on a variant row belong to that variant. |
pack_size | No | For a pack-size variant: the size in unit_type units, so weight labels keep working. |
attributes | No | Details shown on the product page: Material=Cotton|Origin=India. |
Column order doesn't matter; the header row is matched by name. Any column not listed above is ignored.
A minimal file
name,price,original_price,unit_value,unit_type,stock,categories,brand,is_popular
Amul Gold Milk,34,,500,ml,120,Dairy & Eggs|Breakfast,Amul,true
Tata Salt,28,30,1000,g,80,Staples,Tata,false
Bananas (Robusta),49,59,6,pcs,40,Fruits & Vegetables,,true
Note the third row: an empty brand is unbranded, and original_price above
price is what makes a product show a discount.
A product with variants
One row per variant, sharing the product's name (or external_id). The option
names go on the first row; every row carries its own value, price and stock:
name,external_id,price,stock,sku,categories,option1_name,option1_value,option2_name,option2_value
Classic Tee,tee-classic,499,3,TEE-M-RED,Clothing,Size,M,Colour,Red
Classic Tee,tee-classic,499,5,TEE-L-RED,,,L,,Red
Classic Tee,tee-classic,549,2,TEE-XL-RED,,,XL,,Red
The product's own price becomes the cheapest variant's, and its stock the sum.
How rows are matched
The importer decides create-or-update per row, and reports the split before you commit:
If the row has an id
That exact product is updated. Use this when you are keeping a spreadsheet alongside your catalog and pushing changes back to specific products.
If the id is blank but external_id is set
The row is matched on external_id. This is the key to use for anything
that lives in another system — a rename in either place still updates the
same product.
Otherwise
The row is matched against existing products by name. A match updates it; no match creates a new product.
Because name is the fallback match key, renaming a product in your
spreadsheet and re-importing creates a second product rather than
renaming the first. Keep the id column when you intend to edit.
Validation
Every row is checked before anything is written. The dialog lists each failure with its line number and the reason, and the import button stays disabled until the file is clean. The common rejections:
price must be a number greater than 0— a blank, a currency symbol, or a thousands separator in the cell.original_price is below price— a discount cannot be negative.unit_type must be one of g, ml, pcs (or blank)— anything else, includingkgandlitre. Convert the units: 1 kg is1000+g.stock must be a whole number— decimals aren't stock.- A category or brand name that doesn't exist in this store.
- An alcohol or tobacco product — banned platform-wide, and refused on import exactly as it is in the product form. See What you can't sell.
My spreadsheet exports prices as ₹1,299.00
Strip the symbol and the separator before importing — the cell must be a bare
number like 1299. In Google Sheets, set the column's format to Number →
Plain text before exporting; in Excel, format it as Number with no
thousands separator.
Can I import product images?
Yes, as URLs — put a public https link in image_url and the storefront
loads it directly. The importer doesn't upload files, so images hosted only
on your own machine need to go somewhere public first. Images added through
the console's product form are uploaded and hosted for you.
How big can a file be?
Keep a single import at or under 500 rows. Up to that size the whole import commits as one batch — it either all lands or none of it does. Above it, the importer writes in chunks of 500, so a failure part-way through leaves the earlier chunks already written and reports Import stopped after N of M. Validation still covers the whole file first either way; it is only the writing that stops being all-or-nothing. Split large catalogs into files of 500 rows or fewer.
Can I export my existing catalog to a CSV?
Not from the console — there is no export button today. The Download sample button in the import dialog gives you a valid file with your store's own categories and brands in it, which is the closest thing: use it as the header row and fill in your products. If you need a full copy of your data, ask us and we'll provide one.