Skip to main content
Bulk import lets you load commerce data from CSV files — tax classes, tax rates, products, variants, product recipes (the materials each product is made from), customers, orders, order items, and the delivery history of orders that already shipped. You declare the files, upload each one, and submit once. We validate the whole batch, and if it’s clean, commit it. You find out the result by webhook (import.completed / import.failed); you can also poll. It’s part of the developer plan. A batch can touch several resources, so the key needs create and update access on each one the files you declare will write — or full access.

The shape of it

You don’t drive validation and commit separately — that happens on our side, all-or-nothing. You submit once and get told what happened.

1. Get the templates

Each file has an exact set of columns. Fetch the catalog so you build them correctly:
Each template gives you the exact header (keep the columns in order, don’t rename them) and the type and rules for every column. A few conventions across all files:
  • IDs and references are UUIDs. Keep them consistent across files — an order item’s order must match an order’s id.
  • Money is integer minor units: 4500 means 45.00.
  • Dates are ISO-8601, e.g. 2024-01-15T09:00:00Z.
  • Booleans are lowercase true / false.
  • JSON cells must be valid JSON.

2. Create the job

Tell us which files you’re sending:
You get back an import_id and an upload target per file:

3. Upload each file

PUT each CSV straight to its url. This goes to storage, not through the API, so large files are fine. Don’t add your API key to this request — the URL is already authorized.
The URLs expire after about an hour. If one lapses, create the job again for fresh URLs.

4. Submit

You get a 202 immediately. From here, watch for the webhook.

What you get back

Check a job anytime with GET /imports/:id. The status moves through validating → importing → completed, or stops at invalid (validation found problems) or failed. On success, the summary has per-file counts:
If validation fails, the job is invalid and nothing is written. A job that passes validation can still finish completed with a few skipped_rows — rows the commit could not write, such as a recipe naming a material that doesn’t exist. They come back in the same errors list, with their row numbers. GET /imports/:id/errors returns each problem with the file, row and column:
Row numbers are 1-based and count the header as line 1, so the first data row is line 2. Fix the files, re-upload, and submit again.

Good to know

  • Re-running is safe. Rows are matched by the id you supply, so submitting the same data again updates rather than duplicates. That makes import a fine way to keep data in sync, not just a one-time load.
  • Categories and tags are by name. For products, category and tags are plain names — we create them if they don’t exist yet.
  • Statuses must already exist. Fields like a product’s status or an order’s payment_status have to be values your workspace already uses; we won’t invent them.
  • Submitting twice at once returns 409 while a job is still running — wait for it to finish.
The full request and response details are in the Commerce API reference.