Catalogue CSV
GET /api/admin/products.csv writes the whole catalogue, drafts included, one
row per variant with the product’s own fields repeated across its rows. That is
the shape Shopify exports, so the two files can be diffed. Export CSV and
Import CSV on the Products screen are the same thing with a preview attached.
Columns
Section titled “Columns”slug, name, kind, description, bullet_points, seo_title,
seo_description, tax_code, option1_name / option1_value through
option3_*, variant_sku, variant_price_cents, variant_compare_at_price_cents,
variant_inventory_type, variant_inventory_quantity, variant_weight_grams,
is_live, image_paths, variant_image_paths.
Lists inside one cell, bullet points and image paths, are |-separated, because
the comma is taken. Prices are integer cents, and a decimal in a *_cents column is
refused by name rather than rounded. An example file.
Importing is two requests
Section titled “Importing is two requests”POST /api/admin/products/import/validate → { rows, creates, updates, errors[], products[] }POST /api/admin/products/import/commit → { created, updated, skipped }Both take the file as the request body with Content-Type: text/csv, parsed as a
stream; the caps are 5 MB and 5,000 rows. Validation writes nothing and reports
every error at once, each with its row and column. A merchant fixing a 500-row
file one error per attempt gives up. If anything fails, the whole file is refused
unless ?skipInvalid=true, which the preview offers and defaults to off; skipping
is per product, since half a variant matrix is not a product. The commit
re-parses and re-validates rather than trusting a token from the preview.
Before importing over a live catalogue
Section titled “Before importing over a live catalogue”- Products are matched by
slug. Present is an update, absent is a create. - A column the file omits leaves the stored value alone. A three-column price list will not blank every description. A column that is present but empty does clear the field.
- A variant keeps its id when its SKU matches an existing variant’s, or, for a variant with no SKU, when its option values still match, so an update does not orphan the Stripe Price behind it. An import refuses to collapse a product’s options by leaving their columns out.
- An import never writes to Stripe. Imported products land as drafts unless
is_livesays otherwise, and even a live one is not published until someone publishes it. - Images and option groups are not managed by the file.
image_pathsandvariant_image_pathsare written on export and ignored on import.
Spreadsheet safety
Section titled “Spreadsheet safety”Fields whose first character is =, +, - or @ are prefixed with an apostrophe
on export, because a product named =HYPERLINK(...) is a live formula the moment
the file opens in Excel. The file starts with a UTF-8 BOM so Excel reads accented
names correctly.