Skip to content

Catalog import and migration

catalog-import-and-migration

Import, export, and migrate product catalogs with stable SKU keys, a dry run, parent-before-variation order, batched writes, and URL redirects. Use when moving a store between platforms, bulk importing CSV or JSON products, or exporting a catalog for another system.

Version 0.3.2  ·  MIT  ·  Source  ·  Rule

Install this skill

Copy the skill and its rule into your project:

npx @sadrmanesh/commerce-agent-skills add catalog-import-and-migration

The skill goes to .agents/skills/ and the rule to .agents/rules/. See Command line for other directories.

An import is a repeatable job, not a one-off script. Run it against a copy first, make it safe to run twice, and keep a report of every row. Column maps between platforms are in the field mapping notes.

Procedure

  1. Export the source with every field you need: products, variations, attributes, categories, images, stock, prices, SEO fields, and old URLs. Keep the raw export unchanged.
  2. Map fields to the target in one file. Normalize units, currencies, decimals, and booleans there, not in ad hoc fixes.
  3. Choose a stable key. Use the SKU when every sellable unit has a unique one; otherwise keep the source id in a dedicated field such as meta:source_id. Never key on the product name.
  4. Check every row before writing anything: node scripts/check-import-rows.mjs rows.json reports duplicate SKUs, variations without a parent, invalid prices, and negative stock.
  5. Do a dry run that reports creates, updates, and skips per row without writing.
  6. Import in order: categories and attributes, then parent products, then variations, then images and relations (upsells, bundles). A variation needs its parent's id.
  7. Write in batches (the WooCommerce batch endpoint takes up to 100 items per request) and record the target id for every source key, so a rerun updates instead of duplicating.
  8. Create 301 redirects from every old product and category URL to the new one.
  9. Compare counts and totals: products, variations, stock units, and a sample of prices.

Idempotent runs

Look up each row by its key before deciding create or update. Store source key -> target id after every batch. A crash halfway through then resumes without duplicates.

Images

Download source images once, deduplicate by content hash, and upload them to the target media library before linking them. Hotlinking to the old store breaks when it shuts down.

Exports

Export with a documented column set and UTF-8. Include ids, SKUs, and parent references so the file can be imported back. Use . as the decimal separator and ISO 8601 dates. Escape fields with quotes according to RFC 4180 when writing CSV.

Gotchas

  • Spreadsheet programs strip leading zeros from SKUs and barcodes and turn long numbers into scientific notation. Treat them as text.
  • Importing variations before their parent creates orphans or fails. Sort parents first.
  • Prices with a comma decimal (19,90) parse as 1990 or fail in some importers.
  • Running the import twice without a key creates every product twice.
  • Missing redirects lose search rankings and break links in old emails.
  • Customer passwords usually cannot move between platforms. Plan a reset email instead of copying hashes the target cannot read.

Verification

  • The row checker reports no errors before the real run.
  • A second run of the same file updates rows and creates nothing.
  • Product, variation, and stock totals match the source.
  • Every old URL in a sample returns 301 to the right new page.
  • A variable product shows the same options and per-variation prices as the source.

Evaluation

Worked examples and the checks they must satisfy are in evals/tasks.json.

References

Field mapping

WooCommerce CSV importer columns

The built-in product CSV importer reads these columns, among others.

Column Notes
ID Existing product id. Leave empty to create.
Type simple, variable, variation, grouped, external
SKU Unique per product and variation. Used to match existing products when "Update existing products" is checked.
Name Product name. Variations can leave it empty.
Parent For a variation: the parent's SKU, or id:123
Regular price, Sale price Decimal with .
Stock, In stock? Quantity, and 1 or 0
Categories Comma-separated, > for hierarchy: Clothing > Shirts
Images Comma-separated URLs. The first is the main image.
Attribute 1 name, Attribute 1 value(s), Attribute 1 visible, Attribute 1 global Variable products list all values. Variations list their one value.
Meta: _custom_key Custom meta

For more control, use the REST API: create the parent with POST products, then variations with POST products/{id}/variations/batch.

Common platform mappings

Concept WooCommerce Shopify Generic JSON
Product product product product
Sellable unit variation variant variant
Option attribute option (up to 3 per product) options
Price regular_price price price
Compare-at price regular_price with a sale_price compareAtPrice listPrice
Stock stock_quantity inventory level per location stock
Handle or slug slug handle slug

Shopify allows at most three options per product, and a limited number of variants per product, so a WooCommerce product with four attributes must be split or flattened before migrating.

Report file

Write one line per source row:

{"key":"TEE-BLUE-M","action":"update","targetId":5123,"warnings":[]}

Evaluation

Fixtures from evals/tasks.json. See evaluation fixtures for how the checks work.

rerunnable-import

Write the plan for importing 20,000 products with variations from a CSV into WooCommerce so that the job can crash and be run again safely.

Must include: SKU, dry run, Parent, batch, source key

Must not include: match products by name

Worked example

Use the SKU as the key and check every row first: duplicate SKUs, variations whose Parent is missing, and comma decimals. Then do a dry run that reports create, update, or skip for each row without writing.

Import categories and attributes, then variable parents, then variations under each parent. Write with the batch endpoint, 100 items per request, and after each batch save the map from source key to WooCommerce id. If the job crashes, rerunning it looks up each SKU first and updates instead of creating duplicates.

platform-migration-urls

A store moves from one platform to another and the product URLs change. What keeps search rankings and old links working?

Must include: 301, redirect

Must not include: 302

Worked example

Export every old product and category URL with its source id. After the import, map each source id to the new URL and create a permanent 301 redirect for each.

Test a sample of old URLs, including ones from email campaigns and the sitemap, and check each returns 301 to the right page. Submit the new sitemap so search engines move their index to the new URLs.