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:
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¶
- 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.
- Map fields to the target in one file. Normalize units, currencies, decimals, and booleans there, not in ad hoc fixes.
- 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. - Check every row before writing anything:
node scripts/check-import-rows.mjs rows.jsonreports duplicate SKUs, variations without a parent, invalid prices, and negative stock. - Do a dry run that reports creates, updates, and skips per row without writing.
- Import in order: categories and attributes, then parent products, then variations, then images and relations (upsells, bundles). A variation needs its parent's id.
- 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.
- Create 301 redirects from every old product and category URL to the new one.
- 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:
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.