API Reference
Back to site
Catalog

Products

6. Products API

GET/productsList Products

Query Parameters:

ParameterTypeDefaultDescription
pageinteger1Page number
limitinteger20Items per page
searchstring-Search by name or SKU
statusstring-Filter: active, draft, archived
category_idinteger-Filter by category
brand_idinteger-Filter by brand
sort_bystringcreated_atprice, name, created_at, updated_at, popularity_score. Anything else falls back to created_at
sort_orderstringdescasc or desc

Example Request:

bash
curl -X GET "https://your-store.com/api/v1/products?page=1&limit=20&status=active" \
  -u "ck_xxx:cs_yyy"

Example Response (200):

json
{
  "status": "success",
  "data": {
    "products": [
      {
        "id": 1,
        "name": "Organic Flour 1kg",
        "slug": "organic-flour-1kg",
        "sku": "FLOUR-001",
        "price": "12.99",
        "sale_price": null,
        "stock": 100,
        "stock_status": "instock",
        "status": "active",
        "product_type": "simple",
        "description": "Premium organic flour",
        "short_description": "1kg bag",
        "category": { "id": 1, "name": "Flour" },
        "brand": { "id": 1, "name": "Organic Co" },
        "images": [
          { "id": 1, "url": "https://cdn.example.com/flour.jpg", "alt": "Flour" }
        ],
        "tags": [],
        "variants": [],
        "popularity_score": 42,
        "created_at": "2026-01-15T10:00:00.000Z",
        "updated_at": "2026-01-15T10:00:00.000Z"
      }
    ],
    "total": 50,
    "page": 1,
    "limit": 20
  }
}

Variable products in the list: Each product in products includes a variants array. For simple products it is []. For variable products it contains one object per variant (see Variant response shape).


GET/products/:idGet Single Product

Example Request:

bash
curl -X GET "https://your-store.com/api/v1/products/1" \
  -u "ck_xxx:cs_yyy"

Example Response (200):

json
{
  "status": "success",
  "data": {
    "product": {
      "id": 1,
      "name": "Organic Flour 1kg",
      "slug": "organic-flour-1kg",
      "sku": "FLOUR-001",
      "price": "12.99",
      "sale_price": null,
      "stock": 100,
      "stock_status": "instock",
      "status": "active",
      "product_type": "simple",
      "description": "Premium organic flour",
      "short_description": "1kg bag",
      "category_id": 1,
      "brand_id": 1,
      "manage_stock": true,
      "images": [
        { "id": 1, "url": "https://cdn.example.com/flour.jpg", "alt": "Flour", "order": 0 }
      ],
      "variants": [],
      "categories": [{ "id": 1, "name": "Flour" }],
      "tags": [],
      "created_at": "2026-01-15T10:00:00.000Z",
      "updated_at": "2026-01-15T10:00:00.000Z"
    }
  }
}

For variable products, variants is populated with the full variant list; see Variant response shape and Variable Products.


POST/productsCreate Product

Request Body (Simple Product):

json
{
  "name": "Organic Flour 1kg",
  "sku": "FLOUR-001",
  "price": 12.99,
  "sale_price": 9.99,
  "stock": 100,
  "stock_status": "instock",
  "status": "active",
  "description": "Premium organic flour for baking",
  "short_description": "1kg bag",
  "category_id": 1,
  "brand_id": 1,
  "manage_stock": true,
  "images": [
    { "url": "https://cdn.example.com/flour.jpg", "alt": "Organic Flour", "order": 0 }
  ]
}

Field Reference:

FieldTypeRequiredDescription
namestringYesProduct name
skustringYesUnique SKU
pricenumberYesBase price
sale_pricenumberNoSale price (null if no sale)
stockintegerNoStock quantity (default: 0)
stock_statusstringNoinstock, outofstock, onbackorder
statusstringNoactive, draft, archived (default: draft)
descriptionstringNoFull description
short_descriptionstringNoShort description
category_idintegerNoPrimary category ID
category_idsarrayNoMultiple category IDs
brand_idintegerNoBrand ID
tag_idsarrayNoTag IDs
manage_stockbooleanNoWhether to track stock (default: true)
imagesarrayNo[{ url, alt?, order? }]
slugstringNoURL slug (auto-generated from name if omitted)
seo_titlestringNoSEO title
seo_descriptionstringNoSEO meta description
seo_keywordsstringNoSEO keywords

Example Request:

bash
curl -X POST "https://your-store.com/api/v1/products" \
  -u "ck_xxx:cs_yyy" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Organic Flour 1kg",
    "sku": "FLOUR-001",
    "price": 12.99,
    "stock": 100,
    "status": "active",
    "category_id": 1
  }'

Example Response (201):

json
{
  "status": "success",
  "data": {
    "product": {
      "id": 42,
      "name": "Organic Flour 1kg",
      "sku": "FLOUR-001",
      "price": "12.99",
      "stock": 100,
      "status": "active",
      "product_type": "simple",
      "created_at": "2026-03-04T10:00:00.000Z"
    }
  }
}

PUT/products/:idUpdate Product

Same as create; all fields are optional. Only provided fields are updated.

Example Request:

bash
curl -X PUT "https://your-store.com/api/v1/products/42" \
  -u "ck_xxx:cs_yyy" \
  -H "Content-Type: application/json" \
  -d '{
    "price": 14.99,
    "stock": 80,
    "sale_price": 12.99
  }'

DELETE/products/:idDelete Product

Example Request:

bash
curl -X DELETE "https://your-store.com/api/v1/products/42" \
  -u "ck_xxx:cs_yyy"

Example Response (200):

json
{
  "status": "success",
  "message": "Product deleted successfully"
}

GET/products/templateDownload Import Template

Download a CSV template file with all supported product columns and field types for bulk imports.

Auth: API key or admin session

bash
curl -X GET "https://your-store-api.example.com/api/v1/products/template" \
  -u "ck_xxx:cs_yyy"

Example Response (200):

CSV file with headers and sample data.


POST/products/importBulk Import Products

Upload a CSV file to import products in bulk. Supports creating new products or updating existing ones by SKU.

Auth: API key or admin session

Addon: None (core feature)

bash
curl -X POST "https://your-store-api.example.com/api/v1/products/import" \
  -u "ck_xxx:cs_yyy" \
  -F "file=@products.csv" \
  -F "updateExisting=true" \
  -F "createNew=true"
FieldTypeRequiredDescription
filebinaryYesCSV file (max 10MB). Headers: SKU, Name, Description, Price, Sale Price, Stock, Status, Category, Images, etc.
updateExistingbooleanNoDefault: true. Update products with matching SKU.
createNewbooleanNoDefault: true. Create products for rows without matching SKU.
keepExternalImagesbooleanNoDefault: false. Preserve image URLs from external sources (e.g., supplier CDN).
skipExistingImagesbooleanNoDefault: false. Do not update images for existing products.
columnMappingJSON objectNoCustom column-name mapping (for non-standard CSV headers).

Example Response (202):

json
{
  "status": "success",
  "data": {
    "task_id": "import_abc123",
    "message": "Import started in background",
    "filename": "products.csv"
  }
}

Gotchas:

  • Import runs as a background job; use the task ID to poll progress.
  • SKU is the primary key for matching (case-sensitive).
  • Blank Type on an update row converts variable products to simple and deletes variants.
  • Images are fetched from URLs; invalid URLs are silently skipped.
  • CSV must be UTF-8 encoded.

GET/products/exportExport Products as CSV

Export products as a WooCommerce-format CSV that the importer reads back. Pick the columns, the product types and the categories, or export specific products.

Categories come out two ways:

  • Categories: every category the product is in, as full paths, e.g. Kitchen > Fridges > Double Door, Deals. The importer reads this format back.
  • Category L1, Category L2, …: the product's main (deepest) category path, one level per column. There are as many columns as the deepest product needs.

Auth: API key or admin session

bash
curl -X GET "https://your-store-api.example.com/api/v1/products/export?status=published&columns=sku,name,regular_price,categories,category_levels" \
  -u "ck_xxx:cs_yyy" \
  --output products.csv
ParameterTypeDescription
searchstringSearch by name or SKU.
statusstringFilter: published or draft.
category_idintegerFilter by the product's primary category only.
category_idsstringComma-separated category ids. Matches products in these categories or any category below them.
brand_idintegerFilter by brand.
idsstringComma-separated product ids. Export only these.
columnsstringComma-separated column keys: id, type, sku, name, published, featured, visibility, short_description, description, regular_price, sale_price, stock, manage_stock, stock_status, backorders, weight, categories, category_levels, tags, images, brand, attributes, parent, custom_fields. Default: all.
typesstringComma-separated: simple, variable, variation. Default: all.
meta_columnsstring1 writes one Meta: <key> column per custom field instead of the single JSON Custom fields column. The importer reads either.

Example Response (200):

A CSV file. With no options it contains every column, product type and category.


POST/products/bulk-generate-aiGenerate AI Descriptions and SEO

Enqueue a background job to automatically generate product descriptions and SEO metadata using Claude AI.

Auth: API key or admin session

bash
curl -X POST "https://your-store-api.example.com/api/v1/products/bulk-generate-ai" \
  -u "ck_xxx:cs_yyy" \
  -H "Content-Type: application/json" \
  -d '{
    "product_ids": [1, 2, 3],
    "mode": "manual",
    "generate_short_description": true,
    "short_description_format": "paragraph",
    "generate_long_description": true,
    "generate_seo": true
  }'
FieldTypeRequiredDescription
product_idsarrayDependsList of product IDs. Required if mode=manual. Ignored if mode=auto.
modestringNoDefault: manual. auto = find all products missing AI content.
generate_short_descriptionbooleanNoGenerate short product descriptions.
short_description_formatstringNolist or paragraph (only if generate_short_description=true).
generate_long_descriptionbooleanNoGenerate full product descriptions.
generate_seobooleanNoGenerate SEO title, description, and keywords.

Example Response (202):

json
{
  "status": "success",
  "data": {
    "task_id": "ai_gen_xyz789",
    "job_id": "ai_gen_xyz789",
    "message": "Generation started in background",
    "count": 3
  }
}

Gotchas:

  • Job runs asynchronously; poll task status for progress.
  • auto mode finds products with no short description, no long description, or missing SEO title (any missing = match).
  • Generated content is created as draft fields; you must review and publish.

POST/products/bulk-enhance-imagesGenerate Images with AI

Enqueue a background job to generate or replace product images using Gemini AI.

Auth: API key or admin session

Addon: AI Image Generation (must be enabled and configured with Gemini API key)

bash
curl -X POST "https://your-store-api.example.com/api/v1/products/bulk-enhance-images" \
  -u "ck_xxx:cs_yyy" \
  -H "Content-Type: application/json" \
  -d '{
    "product_ids": [5, 6],
    "mode": "append",
    "custom_prompt": "white background, professional lighting"
  }'
FieldTypeRequiredDescription
product_idsarrayYesList of product IDs to enhance.
modestringNoDefault: append. append = add new images. replace = regenerate all images.
custom_promptstringNoCustom instruction to guide image generation.

Example Response (202):

json
{
  "status": "success",
  "data": {
    "task_id": "img_enh_456def",
    "job_id": "img_enh_456def",
    "message": "AI image enhancement started in background",
    "count": 2,
    "mode": "append"
  }
}

Gotchas:

  • Requires AI Image addon; returns 503 if disabled.
  • Requires Gemini API key configured in Settings → Advanced.
  • Generated images are created as product images but not yet published.
  • Each product counts toward the configured maxBulkSize (default varies by plan).

GET/products/ai-generation-statusPoll AI Generation Status

Get the status of ongoing AI content or image generation jobs.

Auth: API key or admin session

bash
curl -X GET "https://your-store-api.example.com/api/v1/products/ai-generation-status" \
  -u "ck_xxx:cs_yyy"

Example Response (200):

json
{
  "status": "success",
  "data": {
    "status": "running",
    "processed": 2,
    "total": 5,
    "errors": []
  }
}

POST/products/bulk-generate-image-altGenerate Image Alt Text in Bulk

Enqueue a background job to generate alt text and titles for product images.

Auth: API key or admin session

bash
curl -X POST "https://your-store-api.example.com/api/v1/products/bulk-generate-image-alt" \
  -u "ck_xxx:cs_yyy" \
  -H "Content-Type: application/json" \
  -d '{
    "product_ids": [10, 11],
    "mode": "manual",
    "overwrite": false
  }'
FieldTypeRequiredDescription
product_idsarrayDependsList of product IDs. Required if mode=manual. Ignored if mode=auto.
modestringNoDefault: manual. auto = find all images with missing alt/title.
overwritebooleanNoDefault: false. true = regenerate even where alt/title already exist.

Example Response (202):

json
{
  "status": "success",
  "data": {
    "task_id": "img_alt_789ghi",
    "job_id": "img_alt_789ghi",
    "message": "Image alt text & title generation started in background",
    "count": 2
  }
}

Gotchas:

  • Alt text is generated deterministically from product name (no AI call, fast).
  • auto mode finds images with no alt or no title.

POST/products/:id/duplicateDuplicate a Product

Create an exact copy of a product (simple or variable).

Auth: API key or admin session

bash
curl -X POST "https://your-store-api.example.com/api/v1/products/42/duplicate" \
  -u "ck_xxx:cs_yyy"

Example Response (201):

json
{
  "status": "success",
  "data": {
    "product": {
      "id": 99,
      "name": "Organic Flour 1kg-copy",
      "sku": "FLOUR-001-COPY",
      "price": "12.99",
      "status": "draft",
      "product_type": "simple"
    }
  }
}

Gotchas:

  • Duplicate is created as draft and receives "-copy" suffix on the name.
  • Images, variants, categories, and tags are all duplicated.
  • SKU is suffixed with "-COPY" to avoid duplicates.

POST/products/:id/generate-image-altGenerate Image Alt Text for One Product

Synchronously generate alt text and titles for all images of a single product.

Auth: API key or admin session

bash
curl -X POST "https://your-store-api.example.com/api/v1/products/42/generate-image-alt" \
  -u "ck_xxx:cs_yyy" \
  -H "Content-Type: application/json" \
  -d '{ "overwrite": false }'
FieldTypeRequiredDescription
overwritebooleanNoDefault: false. true = regenerate even where alt/title already exist.

Example Response (200):

json
{
  "status": "success",
  "data": {
    "product_id": 42,
    "updated": 2,
    "images": [
      { "id": 1, "alt": "Organic Flour 1kg", "title": "Organic Flour 1kg" },
      { "id": 2, "alt": "Organic Flour 1kg", "title": "Organic Flour 1kg" }
    ]
  }
}

Gotchas:

  • This is a synchronous operation (unlike bulk variants).
  • Deterministically derived from product name; no AI involved.

GET/products/:id/currency-pricesGet Currency Price Overrides

Retrieve all currency-specific price overrides for a product.

Auth: API key or admin session

bash
curl -X GET "https://your-store-api.example.com/api/v1/products/42/currency-prices" \
  -u "ck_xxx:cs_yyy"

Example Response (200):

json
{
  "status": "success",
  "data": {
    "prices": [
      {
        "id": 1,
        "product_id": 42,
        "variant_id": null,
        "currency_code": "USD",
        "price": 14.99,
        "sale_price": 12.99
      },
      {
        "id": 2,
        "product_id": 42,
        "variant_id": 5,
        "currency_code": "EUR",
        "price": 13.50,
        "sale_price": null
      }
    ]
  }
}

PUT/products/:id/currency-pricesSet Currency Price Overrides

Create or replace currency-specific price overrides for a product or variant.

Auth: API key or admin session

bash
curl -X PUT "https://your-store-api.example.com/api/v1/products/42/currency-prices" \
  -u "ck_xxx:cs_yyy" \
  -H "Content-Type: application/json" \
  -d '{
    "prices": [
      { "currency_code": "USD", "price": 14.99, "sale_price": 12.99, "variant_id": null },
      { "currency_code": "EUR", "price": 13.50, "sale_price": null, "variant_id": 5 }
    ]
  }'
FieldTypeRequiredDescription
pricesarrayYesArray of price entries.
prices[].currency_codestringYesISO 4217 currency code (e.g., USD, EUR).
prices[].pricenumberYesPrice in this currency.
prices[].sale_pricenumberNoOptional sale price for this currency.
prices[].variant_idintegerNoIf set, override applies only to this variant. Null = base product.

Example Response (200):

json
{
  "status": "success",
  "data": {
    "prices": [
      {
        "id": 1,
        "product_id": 42,
        "variant_id": null,
        "currency_code": "USD",
        "price": 14.99,
        "sale_price": 12.99
      },
      {
        "id": 2,
        "product_id": 42,
        "variant_id": 5,
        "currency_code": "EUR",
        "price": 13.50,
        "sale_price": null
      }
    ]
  }
}

Gotchas:

  • Existing prices for the same currencies are replaced; others are preserved.
  • Currency codes are normalized to uppercase.
  • variant_id=null or omitted = base product price.
  • Invalid prices or currency codes are silently skipped.

Popularity score

Every product carries a read-only popularity_score: the number of units sold across all orders that are paid and not cancelled. It is a plain unit count, not a rate or a weighted ranking, so a product sold 40 times in one order scores the same as one sold once in 40 orders.

The score is recalculated for the whole catalog nightly at 04:00 server time. It is not updated as orders come in, so a product bought this morning keeps yesterday's score until the next run. Writes to popularity_score through POST/PUT /products are ignored — the nightly job is the only writer.

Sort by it with sort_by=popularity_score on the admin API, or sort=best_sellers on the storefront catalog.


POST/products/popularity/recalculateRecalculate scores

Rebuilds popularity_score for every product immediately, instead of waiting for the nightly run. Useful right after a bulk order import, when the scores would otherwise be a day behind.

This rewrites the whole catalog in two statements, so treat it as an occasional operation — it is not something to call per order.

bash
curl -X POST "https://your-store.com/api/v1/products/popularity/recalculate" \
  -u "ck_xxx:cs_yyy"

Example Response (200):

json
{
  "status": "success",
  "message": "Popularity scores recalculated"
}