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 a standalone 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:

json5
{
"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):

json
{
"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:

json5
{
"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:

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

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

json
{
"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:

json
{ "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):

json
{
"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​