original_price is a compare-at price
price is what the customer pays. original_price is the crossed-out price shown next to it, so shoppers can see the markdown.
0, which means no strike-through — the same as leaving the compare-at field empty in the admin. That is deliberate: a product with no promotion should show one price, not a price crossed out beside an identical price.
Variants carry their own price and original_price, and follow the same rule. When a product has variants, the storefront shows the first variant’s pair.
Products created through the API before September 2026 defaulted
original_price to price, which showed exactly that duplicated,
struck-through price. Send "original_price": 0 on those products to clear
it.description supports a small Markdown subset
Product descriptions are not full Markdown or HTML. The storefront renders this much:
Line breaks are preserved as written. Everything else — headings, lists, tables, block quotes, code fences, raw HTML — is not parsed and appears literally,
# and - characters included. A line starting with - reads as a bullet only because the dash is visible, not because a list is rendered.
images is the whole image list
images is an array of URLs in display order, and an update replaces the list:
- Omit
imagesand the current images are left alone. - Send
[]and all images are removed. - Send back the URLs the API returned to reorder without re-uploading. Sending an external URL again fetches it again.
- Up to 10 images per request. A URL we can’t fetch fails the whole request with
400 invalid_request_erroronimages.
variants and options replace their list
Both are lists the product owns outright, so PATCH treats them the same way as images — the array you send becomes the list, in the order you send it.
- An entry with
idedits that variant. The id has to belong to this product. - An entry without
idcreates one. - A variant that already exists and is not in the array is deleted, along with its stock and any back-in-stock requests. Past orders keep their line items.
- Omit the field entirely and the list is untouched.
"variants": []removes them all.
type is fixed once created — it is only read on entries without an id.
Changing inventory_quantity on an existing variant is recorded in stock history exactly as /api/v2/inventory_items would record it, so counts stay auditable however you set them.
Deleting a product
DELETE /api/v2/products/{product_id} removes the product, its variants, options and images, and drops it from every category.
"status": "HIDDEN" instead if you may want the product back.
Delete is REST-only. The MCP server exposes reads, creates and updates but
not delete, so an AI agent connected to a store cannot remove a catalog.