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
productaccess 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, declareoptions, and give each variant anoption1value that matches — plusneed_variant_image: false. - If
inventory_tracking: true,inventory_policyis also required. imagesmust 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 withauto_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"
}
}
}
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>.
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.