This guide covers a code-based approach for migrating data from BigCommerce to Swell, and how to handle some of the quirks specific to BigCommerce's APIs. It's intended to accompany our Migrate to Swell guide, which covers the general order of operations, Swell IDs, custom fields, and the $migrate flag.

We'll use the BigCommerce REST API to read your store data. Create a store-level API account in your BigCommerce control panel under Settings → Store-level API accounts, with read scopes for Products, Customers, and Orders. You'll receive an access token, which authenticates via the X-Auth-Token header. You'll also need your store hash — the short identifier in your API path and control panel URL.

BigCommerce's catalog and customers live on the V3 API, while orders remain on the V2 API — so we'll set up clients for both, plus a Swell client to write with:

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

const STORE_HASH = 'your-store-hash';
const headers = { 'X-Auth-Token': 'your-access-token' };

const bcV3 = axios.create({
  baseURL: `https://api.bigcommerce.com/stores/${STORE_HASH}/v3`,
  headers,
});

const bcV2 = axios.create({
  baseURL: `https://api.bigcommerce.com/stores/${STORE_HASH}/v2`,
  headers,
});

Both API versions paginate with limit and page parameters (up to 250 records per request). Following the recommended migration order, we'll cover products, then customers, then orders.

The V3 catalog API can return images and variants inline with include, which saves per-product requests:

let page = 1;
while (true) {
  const { data: response } = await bcV3.get('/catalog/products', {
    params: { limit: 100, page, include: 'images' },
  });
  const products = response.data;
  if (!products.length) break;

  for (const product of products) {
    await swell.post('/products', {
      name: product.name,
      sku: product.sku || undefined,
      price: product.price,
      sale: !!product.sale_price,
      sale_price: product.sale_price || undefined,
      description: product.description,
      active: false,
      stock_tracking: product.inventory_tracking !== 'none',
      stock_level: product.inventory_level || 0,
      images: (product.images || []).map((image) => ({
        file: { url: image.url_zoom || image.url_standard },
      })),
      bigcommerce_id: product.id,
    });
  }

  page++;
}

A few things to note:

  • V3 responses wrap results in a data property, unlike V2.
  • The BigCommerce product id is retained as a bigcommerce_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 BigCommerce-hosted URLs. Verify your images have been fully imported into Swell before closing the BigCommerce store.
  • For products with options, fetch variants with include: 'variants' (or from /catalog/products/{id}/variants) and map their option values into Swell product options and variants. Only enable variant generation for options where stock is tracked per variation.
  • BigCommerce categories are comparable to Swell categories, including nesting. They aren't assigned automatically — create your category tree in the Swell dashboard and assign products in bulk after migrating.
let page = 1;
while (true) {
  const { data: response } = await bcV3.get('/customers', {
    params: { limit: 100, page },
  });
  const customers = response.data;
  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.phone || undefined,
      bigcommerce_id: customer.id,
    });
  }

  page++;
}

Customer addresses live in a separate collection — fetch them from /customers/addresses filtered by customer_id:in and set each account's default shipping address accordingly. As with other platforms, password hashes cannot be transferred, so customers will be prompted to reset their password the first time they log in after migration.

Orders use the V2 API, which has two quirks: line items are a separate request per order, and dates are formatted as RFC 2822 strings rather than ISO 8601, so convert them before posting to Swell.

let page = 1;
while (true) {
  const { data: orders } = await bcV2.get('/orders', {
    params: { limit: 100, page },
  });
  if (!orders || !orders.length) break;

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

    const { data: lineItems } = await bcV2.get(`/orders/${order.id}/products`);

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

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

    await swell.post('/orders', {
      $migrate: true,
      account_id: account?.id,
      items,
      date_created: new Date(order.date_created).toISOString(),
      bigcommerce_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 BigCommerce order data. To maintain order number continuity, set your starting order number under Settings → General in the Swell dashboard.

That covers the core BigCommerce data. For shipments, subscriptions, gift cards, and coupons, see the Migrate to Swell guide, which covers each data type in detail along with the recommended migration order.