Lightspeed to Shopify: converting a product CSV export

“Lightspeed” is three different products with three different export formats. Everything else about this migration is easy by comparison, so start by working out which one you have.

Open the converter with this presetField names verified against a published export spec.

Where the file comes from

Shopify’s migrating-from-Lightspeed guide gives a different path and a different field table per series, and names the download LightspeedProductDownload.csv in each case:

  • X-series — Catalog → Products, then Export list. Headers are snake_case.
  • E-series — Catalog → Products, then Import or Export Products → Export all. You are asked to pick a delimiter: semicolon, comma or tab. Headers are title case.
  • R-series — Inventory → Item Search, optionally filter, then Export. Only the products in the search are exported.

This preset follows the X-series table, whose columns are handle, Name, description, supplier_name, product_category, tags, sku, variant_option_one_name / variant_option_one_value (and two, three), track inventory, inventory_Main_Outlet, retail_price, supply_price and active.

Pitfall 1: the three series share no column names

This is the first and biggest trap, because the failure is silent. X-series writes Name and retail_price; E-series writes Product name, Brand, Categories, Weight and Quantity. Apply the X-series preset to an E-series file and almost nothing matches — but Shopify’s only hard requirement is Title, so what you get is a file of rows with no title, rejected one by one as “did not contain product data”.

Open your export and read the header row before you trust any preset. If it is E-series, re-point the mapping in step 3: Product name → Title, Brand → Vendor, Categories → Type, SKU → SKU, Weight → Weight, Quantity → Inventory quantity. The auto-match will get most of it, because those names are the ordinary ones.

Pitfall 2: handle is a handle, and Shopify validates it

X-series exports a handle column, and Shopify’s guide warns that handles “can contain letters, dashes, and numbers, but no spaces, accents, or other characters, including periods”, telling you to “edit the Lightspeed handle to ensure that it conforms”.

Lightspeed handles commonly contain underscores — stout_nitro_440 — which Shopify rejects outright: an underscore is not a letter, a digit or a dash. The converter slugifies every handle, so that becomes stout-nitro-440, and the validator reports anything it cannot repair. Because handle repeats on every variant row of a product, it is also the grouping key, and the preset pins grouping to it.

Pitfall 3: stock lives in a column named after your outlet

Shopify’s table names the source column inventory_Main_Outlet. That is not a fixed header — it is inventory_ plus the name of your outlet, so a two-shop business has inventory_Main_Outlet and inventory_Harbour_Road side by side, and a store that renamed its outlet has neither.

The preset maps inventory_Main_Outlet because that is the documented default; check it against your file in step 3. More importantly, Shopify’s product CSV holds one inventory quantity, for a single location. If you have several outlets you have to decide whether to import one location’s stock or the total, and then use Shopify’s separate inventory CSV to set the rest — the guide notes the same limit, and that the Store Migration app “doesn’t import inventory by location” either.

Pitfall 4: active is not Shopify’s Status, quite

Shopify’s table maps active → Status and is specific about the permitted values: “Ensure your import contains only the following values: active, draft, archived”. Lightspeed’s column is closer to a boolean, and real exports carry TRUE, 1 or enabled depending on the version.

Anything outside the three words is rejected with a status error. The converter normalises the common synonyms — enabled, publish and published all become active, pending and private become draft — and the validator reports Status “…” is not allowed for anything it cannot place. If you would rather review everything before it goes live, set the default status to draft in step 2 and leave the column unmapped.

Pitfall 5: retail_price and supply_price, and no weight at all

retail_price is the selling price and supply_price is your cost — Shopify’s table maps the latter to Cost per item, which is margin reporting and never shown to a customer. Both are plain numbers in X-series, but POS exports frequently pick up a currency code (EUR 3.20) when the file has passed through a spreadsheet. Shopify rejects that as not-a-number; the converter strips the code and the symbol.

There is no weight column in the X-series table at all, which matters if you use carrier-calculated shipping: those products import weighing nothing and every quote is wrong. E-series does export Weight (Shopify’s table says to “convert the weight to grams”). If you are on X-series, weight is data you will be adding in Shopify.

What the preset maps

handle → URL handle, Name → Title, description → Description, supplier_name → Vendor, tags → Tags, sku → SKU, retail_price → Price, supply_price → Cost per item, inventory_Main_Outlet → Inventory quantity, active → Status, and the three variant_option_…_name/_value pairs → Option1–3.

product_category is deliberately unmapped. Shopify’s table sends it to Product Category, but warns that the column “should contain values from Shopify’s standard product categories” — a taxonomy breadcrumb or ID — and a Lightspeed category name will not match one. An unmatched value is rejected as “not a valid product category”, so mapping it would trade a missing field for a failed import. Use Type for your own free-text category instead. track inventory is handled by the tool’s own inventory-tracker default rather than mapped, since Shopify wants the literal string shopify there and not a boolean.

Before you upload to Shopify

  • Import into a development store or a draft-status batch first. Set the default status to draft in the tool and nothing goes live by accident.
  • Shopify caps a product CSV at 15 MB. Bigger catalogues are split into numbered parts, and a product is never split across two files.
  • Overwriting existing products by handle is destructive: a blank cell can erase data that is currently there. Export your live products first if you are updating rather than creating.

Full column contract: the Shopify product CSV format, column by column. Nexum Gate is not affiliated with Shopify Inc.