# Migrate from WooCommerce

Source: https://developers.swell.is/guides/migrate-from-woocommerce

This guide covers a code-based approach for migrating data from WooCommerce to Swell, and how to handle some of the quirks specific to WooCommerce and WordPress. It's intended to accompany our [Migrate to Swell](https://developers.swell.is/guides/migrate-to-swell) guide, which covers the general order of operations, Swell IDs, custom fields, and the `$migrate` flag.

### Authentication

We'll use the WooCommerce REST API to read your store data. To create credentials, go to WooCommerce → Settings → Advanced → REST API in your WordPress admin and add a key with Read permission. You'll receive a consumer key (`ck_...`) and consumer secret (`cs_...`), which authenticate as basic auth credentials over HTTPS.

### Setup

Create a Node.js script with two API clients — one reading from WooCommerce, one writing to Swell:

```javascript
const axios = require('axios');
const swell = require('swell-node').createClient(
  'your-store-id',
  'your-secret-key'
);

const woo = axios.create({
  baseURL: 'https://your-site.com/wp-json/wc/v3',
  auth: {
    username: 'ck_...',
    password: 'cs_...',
  },
});
```

The WooCommerce API returns up to 100 records per request using the `per_page` and `page` parameters, which the examples below use to paginate through each collection. Following the recommended migration order, we'll cover products, then customers, then orders.

### Products

```javascript
let page = 1;
while (true) {
  const { data: products } = await woo.get('/products', {
    params: { per_page: 100, page, status: 'publish' },
  });
  if (!products.length) break;

  for (const product of products) {
    await swell.post('/products', {
      name: product.name,
      slug: product.slug,
      sku: product.sku || undefined,
      price: parseFloat(product.price || 0),
      description: product.description,
      active: false,
      stock_tracking: product.manage_stock,
      stock_level: product.stock_quantity || 0,
      images: product.images.map((image) => ({
        file: { url: image.src },
      })),
      woocommerce_id: product.id,
    });
  }

  page++;
}
```

A few things to note:

- The WooCommerce product id is retained as a `woocommerce_id` custom field — the orders script below uses it to reconnect line items to the migrated products.
- Products are created with `active: false` so you can review them before they become visible to customers.
- Images are referenced by their WordPress-hosted URLs. Verify your images have been fully imported into Swell before retiring the WordPress site or its media library.
- For variable products, fetch each product's variations from `/products/{id}/variations` and map the WooCommerce attributes into Swell product `options` and `variants`. Only enable variant generation for options where stock is tracked per variation.
- WooCommerce product categories are comparable to Swell categories. Like other platforms, they aren't assigned automatically — create your categories in the Swell dashboard and assign products in bulk after migrating.

### Customers

```javascript
let page = 1;
while (true) {
  const { data: customers } = await woo.get('/customers', {
    params: { per_page: 100, page },
  });
  if (!customers.length) break;

  for (const customer of customers) {
    await swell.post('/accounts', {
      email: customer.email,
      first_name: customer.first_name,
      last_name: customer.last_name,
      phone: customer.billing?.phone || undefined,
      shipping: customer.shipping?.address_1
        ? {
            address1: customer.shipping.address_1,
            address2: customer.shipping.address_2 || undefined,
            city: customer.shipping.city,
            state: customer.shipping.state,
            zip: customer.shipping.postcode,
            country: customer.shipping.country,
          }
        : undefined,
      woocommerce_id: customer.id,
    });
  }

  page++;
}
```

WordPress password hashes cannot be transferred to Swell, so customers will be prompted to reset their password the first time they log in to their account after migration. Billing addresses and saved payment methods are handled separately — see the payment migration section of the Migrate to Swell guide.

### Orders

Orders reference the products and customers you've already migrated, using the `woocommerce_id` fields to look up the new Swell records:

```javascript
let page = 1;
while (true) {
  const { data: orders } = await woo.get('/orders', {
    params: { per_page: 100, page },
  });
  if (!orders.length) break;

  for (const order of orders) {
    const account = await swell.get('/accounts/:first', {
      woocommerce_id: order.customer_id,
    });

    const items = [];
    for (const lineItem of order.line_items) {
      const product = await swell.get('/products/:first', {
        woocommerce_id: lineItem.product_id,
      });
      if (!product) continue;

      items.push({
        product_id: product.id,
        quantity: lineItem.quantity,
        price: parseFloat(lineItem.price),
      });
    }

    await swell.post('/orders', {
      $migrate: true,
      account_id: account?.id,
      items,
      date_created: `${order.date_created_gmt}Z`,
      woocommerce_id: order.id,
    });
  }

  page++;
}
```

The `$migrate` flag allows setting the original `date_created` and skips validation, events, and webhooks for historical records — see the Migrate to Swell guide for its tradeoffs. Depending on your needs, you can map additional order fields such as totals, taxes, and payment status from the WooCommerce order data. To maintain order number continuity, set your starting order number under Settings → General in the Swell dashboard.

### Next steps

That covers the core WooCommerce data. For shipments, subscriptions, gift cards, and coupons, see the [Migrate to Swell](https://developers.swell.is/guides/migrate-to-swell) guide, which covers each data type in detail along with the recommended migration order.
