Skip to main content

Create and update products

Create products with the Open API, update their fields, publish or unpublish them, and delete them. A product created here starts as a draft and becomes visible on the storefront when you publish it.

When to use this

Publishing new products from your app, bulk importing a catalog, or building a product management back office.

Prerequisites

  • A working public app with OAuth and token storage — follow Develop app first. Every request below carries an Access-Token: {token} header, using the token you saved for each shop.
  • The product access scope granted during OAuth.

How it works

A product is built with a single call: options, variants, and images are all nested inside the same request.

  • For a product with options (like size or color), set has_only_default_variant: false, declare options, and give each variant an option1 value that matches — plus need_variant_image: false.
  • If inventory_tracking: true, inventory_policy is also required.
  • images must contain at least one image.
  • The product starts as a draft. Nothing is visible on the storefront until you set published: true (or schedule it with auto_publish_at).

Create a product

A product is created in one call: the request nests everything under product. title, has_only_default_variant, images, and variants are required, and each variant needs a price.

POST /openapi/2026-01/products

Request body:

{
"product": {
"title": "Summer cotton T-shirt",
"published": false, // start as a draft; publish later
"has_only_default_variant": false,
"need_variant_image": false, // required when has_only_default_variant is false
"inventory_tracking": true,
"inventory_policy": "deny", // required when inventory_tracking is true
"options": [{ "name": "Size", "values": ["S", "M"] }],
"images": [{ "src": "https://cdn.example.com/tshirt.jpg" }], // at least one image
"variants": [
{ "option1": "S", "price": 19.9, "sku": "TS-S", "inventory_quantity": 100, "position": 1 },
{ "option1": "M", "price": 19.9, "sku": "TS-M", "inventory_quantity": 100, "position": 2 }
]
}
}

Response (excerpt, created product under data.data.product):

{
"code": "Success",
"data": {
"product": {
"id": "4d3e7859-455c-4899-a296-7befe731c585",
"title": "Summer cotton T-shirt",
"handle": "summer-cotton-t-shirt",
"inventory_tracking": true,
"inventory_policy": "deny",
"options": [
{ "id": "bca26b9d-c3f1-48ac-93aa-e4a975c44dce", "name": "Size", "values": ["S", "M"], "position": 1 }
],
"variants": [
{ "id": "28ea5ee4-5b1d-4a9d-a92e-e60f0dff3da8", "title": "S", "option1": "S", "position": 1, "price": 19.9, "sku": "TS-S", "inventory_quantity": 100 }
],
"price_min": 19.9,
"price_max": 19.9,
"created_at": "2026-07-02T02:57:25Z",
"updated_at": "2026-07-02T02:57:25Z"
}
}
}
note

images must contain at least one item — an empty array is rejected with value must contain at least 1 item(s). Host the image somewhere public and pass its URL as src. See Create product for the full request body.

Get a product

Fetch one product by id.

GET /openapi/2026-01/products/{id}

Response (excerpt): the full product object, including options[], images[], variants[], and primary_image, alongside id, title, handle, inventory_tracking, inventory_policy, price_min, price_max, created_at, updated_at.

Full fields: see Get product.

Update a product

Change product-level fields (title, description, tags, and so on) with PUT. Only the fields you send are changed.

PUT /openapi/2026-01/products/{id}

Request body:

{
"product": {
"title": "Summer cotton T-shirt (2026)" // only the fields you want to change
}
}

Returns the updated product. Editable fields are listed in the Update product reference.

Publish and unpublish

Publishing is just the published flag. Set it to true to make the product visible on the storefront, false to hide it again — the same PUT endpoint, both directions.

PUT /openapi/2026-01/products/{id}

Request body:

{
"product": {
"published": true // false to unpublish
}
}

Response (excerpt, same product shape as create, with published_at now set):

{
"code": "Success",
"data": {
"product": {
"id": "4d3e7859-455c-4899-a296-7befe731c585",
"title": "Summer cotton T-shirt",
"published": true,
"published_at": "2026-07-02T02:58:55Z"
}
}
}

After publishing, the product is reachable on the storefront at https://<shop>/products/<handle>.

note

To publish on a schedule, set auto_publish_at (RFC 3339) when creating or updating. To take a product offline automatically when it sells out, set inventory_policy: auto_unpublished. See Create product.

Delete a product

DELETE /openapi/2026-01/products/{id}

Response:

{ "code": "Success", "data": {} }

See Delete product.

To delete many at once, use DELETE /products?product_ids=id1,id2 — see Batch delete products.

List products

The list endpoint uses cursor pagination (cursor plus per_page, max 250) and filters such as title, collection_id, and published_status.

GET /openapi/2026-01/products

Response (excerpt, list under data.data.products, cursor under data.data.cursor):

{
"code": "Success",
"data": {
"cursor": "MjAyNi0wMS0yM1QxMDowODoyN1o...",
"products": [
{
"id": "ac7783d3-df2d-47fe-abca-7ee3d9bceb04",
"title": "T-shirt",
"handle": "t-shirt",
"inventory_quantity": 666,
"price_min": 11,
"price_max": 33,
"created_at": "2026-07-01T09:25:50Z"
}
]
}
}

Full filters: see List products.

To get only the total count, use GET /products/count — see Count products.

Next steps