Moving in from Shopify or WooCommerce
Bringing across your catalogue, your customers and your order history from your current shop, without losing the ranking you already had on Google.
Bringing across your catalogue, your customers and your order history from your current shop, without losing the ranking you already had on Google.
It lives in Panel → Migration. Nothing needs configuring first.
Two ways in
Connect to your shop. Give it the address and a pair of read-only keys and it fetches everything by itself. This is what you want almost always: nothing to export, no files that come out half-finished, and it brings what a CSV cannot — orders, coupons, reviews.
Upload a CSV. For anyone who can't or won't hand over keys to their shop, and for platforms we don't cover yet. It brings products and customers.
Start with the trial
The "A test first" switch is on, and it's worth leaving on: it brings 20 of each thing so you can see how your catalogue looks here before you bring twenty thousand.
Look at it, and if you're happy, undo the trial and run the whole thing. Undoing a trial of twenty products is one button; undoing a full migration pointed at the wrong shop is one button too, but half an hour later and with your heart in your mouth.
The keys you need
Shopify
The address is the one ending in .myshopify.com, not your pretty domain:
the API only understands that one.
The token comes from a private app with read access to:
| Scope | What for |
|---|---|
read_products | catalogue, variants, collections |
read_inventory | weight — yes, weight lives in inventory |
read_customers | customers |
read_orders | order history |
🔴 Without
read_inventorythe whole catalogue arrives with no weights at all, and no error. Shopify moved weight off the variant in 2024 and put it on the inventory item, which needs a different scope. A catalogue with no weights charges every shipment at the base rate. If it happens we tell you at the end — but it's cheaper to grant it up front.
WooCommerce
WooCommerce → Settings → Advanced → REST API → Create key, with read
permission. You get a key (ck_…) and a secret (cs_…).
If your hosting swallows the authentication header — plenty of Apache setups do — we detect it, retry another way and tell you, rather than fixing it behind your back.
What comes across
The first two columns are by connecting; the third, by uploading a CSV.
| Shopify | WooCommerce | CSV | |
|---|---|---|---|
| Products, variants, prices, stock | ✅ | ✅ | ✅ |
| Images (rehosted on your shop) | ✅ | ✅ | ✅ |
| Categories and collections | ✅ | ✅ | ✅ |
| SEO title and description | ✅ | ✅ | ✅ |
| Product custom fields | ✅ | ✅ | — |
| Variant custom fields | ✅ | — | — |
| Customers and addresses | ✅ | — | ✅ |
| Order history | ✅ | ✅ | — |
| Coupons | — | ✅ | — |
| Reviews | — | ✅ | — |
| Blog posts | — | ✅ | — |
| Pages | — | ✅ | — |
| Shipping zones and rates | — | ✅ | — |
| Tax rates | — | ✅ | — |
And, when you upload a product CSV: the redirect map (see below).
🔴 WooCommerce customers do not come across by connecting. Bring them in with the customer CSV WooCommerce exports. Their orders do come across the connection, with the email and addresses from the time, but not linked to a customer record.
Switching orders off switches off more than it looks. With that box unticked, Shopify collections do not come across either, nor WooCommerce shipping, taxes and pages. If you want them, leave it on.
SEO: the part almost nobody takes with them
The SEO title is not the product title. On the page it reads "T-shirt"; on Google it needs to read "Organic cotton t-shirt | My Shop". If you've spent years writing those, they're part of why people find you.
It comes from Shopify's own columns and, on WooCommerce, from Yoast or Rank Math — the two that between them cover almost all of WordPress. With another plugin you lose nothing you had: there simply isn't any.
Redirects: the other half of the job
More than half of the shops that change platform lose Google traffic, and it's almost never a penalty: it's old URLs that stop existing without saying where they went.
We generate one permanent redirect per old URL, one to one. Sending everything to the home page doesn't count: to Google that's an error page in disguise.
It includes Shopify's collection-nested URLs
(/collections/clothing/products/t-shirt), which are half the product traffic
of a Shopify shop and the ones everyone forgets.
The map is generated only from a product CSV: upload the file and it appears, whether or not you then import it. Connecting to your shop does not produce one, so if you migrate by connecting, upload the product CSV as well just to get the map.
It comes out in four formats — nginx, Apache, JSON and CSV — and you can download it before importing anything, which is when you need it: before you touch the DNS.
It covers products and categories. Pages, blog posts and filtered URLs you had indexed are not in there: pull those from Search Console and add them by hand.
Above the download buttons there are two fields, Product path and Category
path, which say where the redirects point to in your new shop. They start at
producto and categoria, which is what the factory themes use. If your theme
puts the product sheet somewhere else — say p — write it there and the map
comes out pointing at /p/<handle>. Write it with or without slashes, whichever
you like: p, /p and /p/ are the same thing.
Each old URL points to /producto/<handle> or /categoria/<handle> (or to
whatever you put in those two fields), with no
language. The factory themes use a language prefix, so they answer those
addresses with a temporary 307 redirect to /en/… or /es/…. The visitor
lands in the right place, but the chain is 301 → 307. If you want a single
permanent hop, edit the downloaded file and add the language to each target
(/en/producto/<handle>).
Orders are archived, not replayed
An order from two years ago comes in as history. It doesn't move stock, doesn't charge anything, doesn't send a single email and doesn't award points.
That isn't a technical detail: a thousand migrated orders moving stock leave your catalogue sold out before you open, and a "your order is on its way" to a customer from 2024 is the worst possible first impression for a shop that has just been born.
- With its original date. With today's, your sales reports lie and your returns window reopens for the entire catalogue at once.
- With its number, if you ask. The new shop's numbering is pushed above it so your first real order doesn't collide with one from two years ago.
- Linked to its customer and its products, so it shows up under "my orders" and you can refund what sold before the switch. On WooCommerce, only to its products: customers do not come across the connection (see above).
- A line for a product you no longer sell still comes in, with its title, SKU and price. That's what you need to help whoever is asking.
The status is translated to pending, completed or cancelled, and the original is kept as it was — Shopify splits status across two fields and WooCommerce merges both into one, so any translation loses something.
Coupons
They come across with their code, their discount, their usage limit and how many times they've already been used — without that, a hundred-use coupon spent ninety-seven times arrives with a hundred left.
An expired one comes across too, but switched off: that way anyone holding it gets "this has expired" instead of "this doesn't exist", and the old orders that used it can still be explained.
🔴 The conditions don't travel yet. Minimum spend, product or category restrictions, email restrictions, "can't be combined"… none of that comes across, and a coupon without its condition discounts where it shouldn't. You get the list with each code on it, so you can check them before you advertise them.
Reviews, blog, pages and collections
Reviews don't go back into moderation. They already went through it on your old shop; putting eight hundred back in the queue means re-checking by hand what was already checked, and what actually happens is that nobody does it and your product pages end up with none. Whatever you marked as spam or binned stays behind.
From the blog you get published posts, not drafts. The blog belongs to WordPress, not WooCommerce, and WooCommerce keys don't authenticate its routes. Posts come with their cover image, their byline and their publication date.
WordPress pages come in under /pagina/<address>, which is not the one
they had there; in the factory themes they are served at /en/pagina/<address>
and /es/pagina/<address>. The Mascotas and Dulce Obrador themes have no page
route, so there they do not show. If they ranked, add those URLs to the
redirect map: the map does not carry them. A child page comes in as parent-child, and if two share an
address, one comes in with a different one and we tell you which.
Shopify's automated collections come across frozen. An automated collection is a rule ("everything under £20") that Shopify re-evaluates by itself. Here it arrives with the products it held on moving day, as a normal category: it works the same on day one and stops updating itself. We tell you which ones, by name.
While it runs
You see the stages, not a percentage: when something is slow, what you need to know is what is slow. You can pause it and carry on later.
Problems appear at the top, in two blocks: what didn't come in and what did come in but is worth a look. They're deliberately not tucked behind a disclosure — a migration that finishes "fine" with twelve warnings is one you need to review before opening.
Undo removes what that migration brought and tells you what it couldn't undo. Staying quiet about that would be worse than not offering it.
Running it again
Days pass between the migration and the DNS switch, and your old shop keeps selling. Run it again and it brings only what's new: what's already here is recognised and updated, not duplicated. Not the products, not the customers, not the history.
Run it as many times as you like.
Things that save you a bad afternoon
- The price that comes across is the one the customer pays, not the struck-through one. Bringing the old price would sell dearer than your original shop.
- Weight lands in your store's weight unit (Settings > General > Weight
unit), converted from whatever it left in. Mistaking pounds for kilos
inflates every shipment by 120%, so the move does the sum for you: Shopify
declares the unit on each variant, WooCommerce is asked for its own
woocommerce_weight_unitsetting, and a CSV column titledWeight (lbs)says it in its own name. Set your weight unit before you move in: it is where every weight will land, and it is a label, so changing it afterwards will not re-do the sums. - If the source will not say what unit it used, the weights are read as kilograms — WooCommerce's factory default — and the move says so in its report, naming how many weights it had to guess for. Check a few products before you open: a shop that worked in pounds or grams would be wrong by that factor, and nothing else would complain.
- Anything hidden comes in as a draft. Archived products on Shopify too: publishing them would reopen something you closed on purpose.
- Passwords don't travel. What Shopify and WooCommerce store are hashes from their own algorithm. Your customers come in as records and create their account with the same email next time they buy. Tell them before you open, or you find out through their messages.
- Email consent is copied, never assumed. Only someone who said so explicitly comes in subscribed; blank or odd is a no, and if an email appears twice the no wins. The GDPR asks for an affirmative act, and "they were already on my list" is not a legal basis.
- Barcodes are unique across the whole shop. If your export repeats one, the first keeps it and the rest are left blank — rather than losing the product altogether. We tell you how many.
- Check your taxes before you open. Every platform stores VAT its own way: look at whether your prices include tax.
Large catalogues
Connecting has no size limit: the job runs in the background, can be paused and resumes where it left off if something is cut. On Shopify a full export is requested and we wait for it to be prepared, which on a large catalogue is minutes.
With CSV the file limit is 25 MB. If your catalogue is bigger, split it: since nothing gets duplicated, you can upload the parts one after another.
WooCommerce shipping and taxes
The zones and their active rates come across, and the tax rates with their country, state and percentage. But it is worth going over all of it before you open, for four reasons:
- A zone comes in covering only its countries. If on WooCommerce you narrowed it by state or postcode, those rules are left out, and we tell you which zone it was. Narrow it again under Shipping.
- Everything comes in as a fixed rate, at the cost it had. One that asked a carrier for a live price comes in fixed too, and shows up in the warnings.
- Free shipping comes in at 0 and for everyone. Its minimum amount does not travel: set it as a condition on the rate, or you give away shipping on every order.
- A cost with a formula (
10 * [qty],[fee]) is not understood and comes in at 0.
🔴 Rate prices always come in as euros, whatever currency your WooCommerce used: the amount is copied as it is and labelled EUR. If you sold in another currency, check every rate. If a country already had a default tax, the one arriving comes in as an additional one and we warn you: check which applies.
How to adjust all of this, in set up shipping.
What doesn't come across yet
From Shopify: coupons, reviews, the blog, pages, shipping and taxes. Of the variant custom fields, only those on variants with a SKU: it is the only thing that lets them be matched up.
From WooCommerce: customers (bring them by CSV), variant custom fields, coupon conditions (see above), blog drafts, shipping coverage by state or postcode, and the free-shipping minimum amount.
From neither: navigation menus and passwords.