# Shopify to GoCommerce Catalogue Import — Open-Source Module

> Import a whole Shopify catalogue into GoCommerce over the Admin API — products, variants, options, pictures and stock. Running it again updates, not copies.

- Canonical: https://kitcommerce.store/integrations/import-shopify/
- Last updated: 2026-09-25

---

Migration

## Import a Shopify catalogue into GoCommerce

A Go package that brings a Shopify catalogue across over Shopify’s Admin API: products with their variants, options, pictures and stock, one product at a time, with a progress bar in the admin. Every product remembers its Shopify id, so running it again updates what it imported rather than making copies.

- **Migration** module
- **2** settings
- **14** tests
- **4** API operations
- **ext/import-shopify**

### What it does

In the package’s own words, registering it adds an “Import from Shopify” drawer to the admin’s products page and three admin routes. An operator puts the shop’s domain and an Admin API access token into the plugin’s settings once, presses the button, and watches a progress bar walk the catalogue.

It is for the Shopify store that still exists. The engine already reads Shopify’s product CSV, which remains the tool for a file somebody was sent; this module reads the API instead, so products arrive with their variants, options, pictures and inventory attached rather than flattened into rows.

Credentials are a custom app in the Shopify admin with the read_products scope. Its access token goes in the plugin’s settings as a secret and is never handed back out. There is no OAuth: as the package puts it, a store importing its own catalogue once is not a public app.

### Configuration

Two settings, both required, both typed on the Plugins screen. The module takes no Config at all — examples/store/main.go explains that a migrating merchant fills them in once on a screen rather than being handed an environment variable for a server they may not have.

*Shopify import module settings — 2 settings, 2 required*

| Setting | Environment variable | Required | What it does |
| --- | --- | --- | --- |
| `shop` Shop domain | — | Yes | The admin domain, acme.myshopify.com — not the storefront’s own. The module forgives https://, a path, a trailing slash, and a bare acme. |
| `access_token` Admin API access token | — | Yes | From a custom app in Shopify’s Settings → Apps → Develop apps, with read_products. Stored as a secret and never returned. |

The module reads its Config struct, not the environment. The variable names are the ones the package’s own example or the reference binary uses; in your own `main()` you choose where each value comes from. Where a setting has a panel label, the admin’s settings drawer can hold it too, and a value typed there wins over Config.

### Setting it up

1. **Create a custom app** — In the Shopify admin, a custom app with the read_products scope. Copy its Admin API access token.
2. **Install the module** — Pass shopify.New() to gocommerce.New in your own main(), as below. The reference binary has no flag for it; examples/store/main.go installs it.
3. **Enter the credentials** — Shop domain and token on the Plugins screen, under Import from Shopify. POST /api/admin/x/import-shopify/check confirms them — shop name, currency, product count — before anything is written.
4. **Import** — Press Import on the products page, or POST /api/admin/x/import-shopify/jobs. The job runs in the background, and GET …/jobs/{id} reports created, updated, failed and warnings.

main.go

```
import (
	"github.com/itswadesh/gocommerce/core"
	shopify "github.com/itswadesh/gocommerce/ext/import-shopify"
)

// No Config: the shop domain and the access token are plugin
// settings, typed in once on the Plugins screen.
app, err := gocommerce.New(cfg, shopify.New())
```

The package doc’s own example, with its imports. cfg is your gocommerce.Config. Import path `github.com/itswadesh/gocommerce/ext/import-shopify`.

### How it works

- **Matched on Shopify’s id**

  Each product records metadata.shopify.id. A second run finds the row by that id — not by handle, which changes when a product is renamed — and updates it. The handle is a fallback only for rows that predate the module.

- **Every page, politely**

  The walk follows Shopify’s Link-header cursors to the end rather than counting pages, waits out a 429 for its Retry-After, and slows down when the call-limit header says the bucket is nearly full.

- **One product at a time**

  Each product commits on its own, so an import that dies halfway has half the catalogue in, and re-running finishes the job. One bad product is a warning on the job, not a stopped import.

- **Money read exactly**

  Shopify sends prices as decimal strings. They are read as two integers, never as a float, with the store currency’s own number of decimals — a yen store is not divided by a hundred.

- **Stock that means what it said**

  A variant Shopify does not track arrives untracked rather than as zero stock, a negative count arrives as zero, and continue-selling is kept. Shopify’s placeholder “Default Title” option is dropped.

- **Pictures linked, not downloaded**

  Images go into the media library by their Shopify URL, with their alt text, in the product’s own order; a picture the library already holds by that URL is reused.

### What it does not do

Read these before an order depends on it. No store is known to run GoCommerce in production yet, so these come from the code, not from anyone’s experience.

- **Products only** — Collections, customers, orders, discounts and pages are not read. Past orders come through the core’s Shopify-format CSV import, which moves no stock and fires no events unless asked.
- **A re-run updates the copy, not the variants** — A second run updates title, description, status, vendor, type and tags. Variants, prices, stock and pictures are not reconciled, so a price changed in Shopify after the first import stays as it was.
- **Prices at face value** — Amounts are taken in the store’s own currency. A shop priced in another currency is imported digit for digit, unconverted — the credential check shows the shop’s currency, so look first.
- **Pictures stay on Shopify’s servers** — They are linked by URL, not copied, so they are only as lasting as those URLs. Variant-specific pictures are not attached to their variants.
- **One stock figure per variant** — Each variant’s inventory quantity arrives as a single on-hand number. Stock per Shopify location is not carried over.
- **API version pinned to 2024-10** — The package pins Shopify’s Admin API version rather than tracking the latest, and by its own note Shopify retires each version after a year. Moving the pin is a deliberate edit to shopify.go.
- **An interrupted job still says running** — Imports run in the background. A restart mid-import leaves that job marked running; starting another updates what arrived and adds the rest.

FAQ

### Questions about the Shopify import module

**Does it import orders and customers?**

No — products only. Order history comes through the core’s Shopify-format CSV import, from the admin’s Import screen or POST /api/admin/import/orders; it takes Shopify’s own orders export, moves no stock and fires no events unless asked. Customers need no import of their own: in GoCommerce a customer is a reading of the orders.

**Can I run it twice?**

Yes, and it is meant to be. Products are matched on their Shopify id, so a second run updates title, description, status, vendor, type and tags and creates only what is new. It does not touch the variants, prices or stock of products it imported before.

**What does it need from Shopify?**

A custom app with the read_products scope, and its Admin API access token. No OAuth and no public app. The token is stored as a secret in the plugin’s settings and is never returned by the API.

**Will my old product URLs still work?**

Shopify’s handle becomes the product’s slug, which is what makes a redirect from the old /products/\<handle> address possible. Whether the new storefront uses the same path is the storefront’s decision.

**Is there an importer for WooCommerce or Magento?**

No. Moving from another platform means a script against GoCommerce’s documented API, or the core’s product CSV import in GoCommerce’s own layout.

### Source

Everything on this page is read from [`ext/import-shopify`](https://github.com/itswadesh/gocommerce/tree/main/ext/import-shopify) in the GoCommerce repository, MIT licensed. When this page and the code disagree, the code is right and this page is out of date.Shopify is a trademark of its owner; this module talks to its public API and implies no endorsement. See [trademarks](https://kitcommerce.store/about/#trademarks).

- [ext/import-shopify on GitHub](https://github.com/itswadesh/gocommerce/tree/main/ext/import-shopify)
- [Shopify](https://www.shopify.com)
- [All GoCommerce modules](https://kitcommerce.store/integrations/)

### Try it against a store of your own

The one-command stack gives you GoCommerce’s API and admin on your own machine in minutes. Add this module to it and try it on test orders before a real one depends on it.

[Deploy in minutes](https://kitcommerce.store/#one-command) · [Read the module](https://github.com/itswadesh/gocommerce/tree/main/ext/import-shopify) · [All integrations](https://kitcommerce.store/integrations/)
