skills/catalog-inventory/catalog-import-export/SKILL.md
Import and export your entire product catalog in bulk using your platform's native tools or dedicated apps, with validation and scheduled sync support
npx skillsauth add finsilabs/awesome-ecommerce-skills catalog-import-exportInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Bulk importing and exporting products is a core catalog management task — whether you're onboarding a new store from a supplier spreadsheet, doing mass price updates, or feeding products to Google Merchant Center. Every major platform has native import/export tools that handle this without custom code. Use them first; only build a custom pipeline if your volume, format, or validation requirements exceed what the platform offers.
| Platform | Recommended Tool | Why | |----------|-----------------|-----| | Shopify | Matrixify (Bulk Product Import Export) | Handles complex catalogs with metafields, variants, multiple images, and collections; supports scheduled sync and error reports | | Shopify (simple) | Shopify's built-in CSV import | Free, sufficient for basic catalogs under a few hundred products with standard fields only | | WooCommerce | WP All Import Pro | Most powerful WooCommerce import tool; supports CSV/XML, field mapping, scheduled imports, and conditional logic | | WooCommerce (built-in) | WooCommerce Product CSV Importer | Ships with WooCommerce; handles products, variations, and images for simple use cases | | BigCommerce | Built-in bulk import / Feedonomics | BigCommerce's native import handles most needs; Feedonomics for multi-channel feed management | | Custom / Headless | Build a custom pipeline | Only if your platform has no native tools or your validation/transformation requirements are too complex |
Regardless of platform, product import files follow a similar structure. Use the platform's sample CSV as your template:
Key fields almost every platform requires:
| Field | Notes |
|-------|-------|
| Handle / Slug | URL-safe unique identifier (e.g., blue-cotton-tshirt) |
| Title | Product name |
| Price | Decimal, e.g., 29.99 |
| SKU | Unique per variant |
| Inventory Quantity | Integer |
| Images | Comma-separated URLs or upload separately |
| Variant options | Size, Color, etc. |
Important formatting rules:
2026-03-12true / false (not yes/no or 1/0 unless the platform specifies)Option A: Built-in CSV import (simple catalogs)
Limitations: No support for metafields, custom collections, or complex variant logic in the built-in importer.
Option B: Matrixify (recommended for complex catalogs)
Exporting for Google Merchant Center:
Option A: Built-in product importer
Option B: WP All Import Pro (recommended for advanced needs)
For Google Merchant Center feed export:
Built-in bulk import:
Product Image URL columnGoogle Shopping feed:
For advanced multi-channel feeds: Install Feedonomics or GoDataFeed from the BigCommerce App Marketplace for Amazon, eBay, and comparison engine feeds.
For headless storefronts, build a pipeline that validates, transforms, and upserts products:
// lib/catalogImport.ts
import { z } from 'zod';
import { parse } from 'csv-parse';
import { createReadStream } from 'fs';
// Define and validate the import schema
const productRowSchema = z.object({
handle: z.string().min(1).regex(/^[a-z0-9-]+$/, 'Handle must be lowercase alphanumeric with hyphens'),
title: z.string().min(1).max(255),
price: z.coerce.number().positive(),
sku: z.string().min(1),
inventory_quantity: z.coerce.number().int().min(0).default(0),
image_url: z.string().url().optional(),
});
// Stream-parse large CSV files without loading into memory
export async function* parseCatalogCsv(filePath: string) {
const parser = createReadStream(filePath).pipe(
parse({ columns: true, skip_empty_lines: true, trim: true })
);
let rowIndex = 2;
for await (const rawRow of parser) {
const result = productRowSchema.safeParse(rawRow);
yield result.success
? { row: rowIndex, data: result.data, errors: null }
: { row: rowIndex, data: null, errors: result.error.issues.map(i => ({ field: i.path.join('.'), message: i.message })) };
rowIndex++;
}
}
// Process as an async job with upsert logic (idempotent, safe to re-run)
export async function runCatalogImport(filePath: string) {
let processed = 0;
const errors: { row: number; errors: { field: string; message: string }[] }[] = [];
for await (const { row, data, errors: rowErrors } of parseCatalogCsv(filePath)) {
if (rowErrors) { errors.push({ row, errors: rowErrors }); continue; }
// Upsert by handle — re-running the same file won't create duplicates
await db.products.upsert({ where: { handle: data!.handle }, create: data!, update: data! });
processed++;
}
return { processed, errorCount: errors.length, errors: errors.slice(0, 50) };
}
For Google Merchant Center XML export:
import { create } from 'xmlbuilder2';
export async function exportGoogleFeed(products: Product[]) {
const root = create({ version: '1.0', encoding: 'UTF-8' })
.ele('rss', { version: '2.0', 'xmlns:g': 'http://base.google.com/ns/1.0' })
.ele('channel');
for (const product of products) {
const item = root.ele('item');
item.ele('g:id').txt(product.sku);
item.ele('g:title').txt(product.title);
item.ele('g:price').txt(`${product.price} USD`);
item.ele('g:availability').txt(product.inStock ? 'in_stock' : 'out_of_stock');
item.ele('g:link').txt(`https://yourstore.com/products/${product.handle}`);
item.ele('g:image_link').txt(product.images[0]);
}
return root.end({ prettyPrint: true });
}
All platforms support a preview/dry-run step — always use it before committing a large import:
Fix all errors listed in the validation step before proceeding. A partial import — where some rows succeed and others fail — is harder to clean up than re-running a fully corrected file.
After importing:
| Problem | Solution |
|---------|----------|
| CSV import fails with encoding errors | Ensure the file is saved as UTF-8 (in Excel: File → Save As → CSV UTF-8); special characters in product names are the most common cause |
| Images don't appear after import | Images must be hosted at publicly accessible HTTPS URLs at import time; Shopify and WooCommerce download them during import |
| Variants not created correctly | Ensure your CSV has one row per variant (not one row per product); Shopify's CSV format uses repeated handle rows for multiple variants |
| Import creates duplicate products on re-run | Use a tool that supports upsert by handle/SKU (Matrixify, WP All Import); avoid tools that only support insert |
| Inventory reset to 0 after product import | Keep inventory out of the product import CSV if you manage it separately; most platforms let you skip inventory columns |
| Google feed rejected by Merchant Center | Verify required fields: id, title, description, link, image_link, availability, price, brand, gtin or mpn |
tools
Let shoppers save products to a wishlist, share it with friends, and get notified when saved items come back in stock or drop in price
development
Build a themeable storefront with design tokens and CSS custom properties that supports white-labeling, multi-brand variants, and dark mode
development
Speed up product discovery with instant search suggestions, fuzzy typo matching, and category-aware results powered by Algolia or Elasticsearch
development
Build a mobile-first storefront with thumb-friendly navigation, sticky add-to-cart buttons, and touch-optimized components for high mobile conversion