> This page is for Developer.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.bigcommerce.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.bigcommerce.com/_mcp/server.

# Update Variants (batch)

PUT https://api.bigcommerce.com/stores/{store_hash}/v3/catalog/variants
Content-Type: application/json

Updates a batch of `variant` objects. Currently the limit is 50 variants however this is subject to change.

**Required Fields**

To update an existing variant:
* id (variant ID)

To create a new variant:
* product_id
* sku
* option_values
  - id (option_value ID - Example: 146)
  - option_id (Option ID - Example: 151)

Reference: https://docs.bigcommerce.com/developer/api-reference/rest/admin/catalog/product-variants/variants-batch/update-variants-batch

## Authentication

- `X-Auth-Token` header (required) — ### OAuth scopes | UI Name | Permission | Parameter | |:--------|:-----------|:----------| | Products | modify | `store_v2_products` | | Products | read-only | `store_v2_products_read_only` | ### Authentication header | Header | Argument | Description | |:-------|:---------|:------------| | `X-Auth-Token` | `access_token` | For more about API accounts that generate `access_token`s, see our [Guide to API Accounts](/developer/docs/overview/api-fundamentals/api-accounts#api-accounts). | ### Further reading For example requests and more information about authenticating BigCommerce APIs, see [Authentication and Example Requests](/developer/docs/overview/api-fundamentals/api-accounts#x-auth-token-header-example-requests). For more about BigCommerce OAuth scopes, see our [Guide to API Accounts](/developer/docs/overview/api-fundamentals/api-accounts#oauth-scopes). For a list of API status codes, see [API Status Codes](/developer/api-reference/rest/overview#rest-http-status-codes).

## Request

### Path parameters

- `store_hash` (string, required) — Permanent ID of the BigCommerce store.

### Headers

- `Accept` (string, required, default: application/json) — The [MIME type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types) of the response body.

### Body (application/json)

This endpoint expects a list of CatalogVariantsPutRequestBodyContentApplicationJsonSchemaItems.

- `list of CatalogVariantsPutRequestBodyContentApplicationJsonSchemaItems`

## Response

### 200

OK

- `data` (list of CatalogVariantsPutResponsesContentApplicationJsonSchemaDataItems, optional)
- `meta` (CatalogVariantsPutResponsesContentApplicationJsonSchemaMeta, optional) — Data about the response, including pagination and collection totals.

## Errors

### 422 Unprocessable Entity Error

This is the result of missing required fields, or of invalid data. See the response for more details.

- `batch_errors` (list of CatalogVariantsPutResponsesContentApplicationJsonSchemaBatchErrorsItems, optional)

## Types

### CatalogVariantsPutRequestBodyContentApplicationJsonSchemaItems

The model for a PUT to update variants on a product.

- `cost_price` (double, optional) — The cost price of the variant. It is not affected by Price List prices. This value displays as null in the control panel when `cost_price` equals zero.
- `price` (double, optional) — This variant’s base price on the storefront. If a Price List ID is used, the Price List value will be used. If a Price List ID is not used, and this value is `null`, the product’s default price (set in the Product resource’s `price` field) will be used as the base price.
- `sale_price` (double, optional) — This variant’s sale price on the storefront. If a Price List ID is used, the Price List value will be used. If a Price List ID is not used, and this value is null, the product’s sale price (set in the Product resource’s `price` field) will be used as the sale price.
- `retail_price` (double, optional) — This variant’s retail price on the storefront. If a Price List ID is used, the Price List value will be used. If a Price List ID is not used, and this value is null, the product’s retail price (set in the Product resource’s `price` field) will be used as the retail price.
- `weight` (double, optional) — This variant’s base weight on the storefront. If this value is null, the product’s default weight (set in the Product resource’s weight field) will be used as the base weight.
- `width` (double, optional) — Width of the variant, which can be used when calculating shipping costs. If this value is `null`, the productʼs default width (set in the Product resourceʼs `width` field) will be used as the base width.
- `height` (double, optional) — Height of the variant, which can be used when calculating shipping costs. If this value is `null`, the productʼs default height (set in the Product resourceʼs `height` field) will be used as the base height.
- `depth` (double, optional) — Depth of the variant, which can be used when calculating shipping costs. If this value is `null`, the productʼs default depth (set in the Product resourceʼs `depth` field) will be used as the base depth.
- `is_free_shipping` (boolean, optional) — Flag used to indicate whether the variant has free shipping. If `true`, the shipping cost for the variant will be zero.
- `fixed_cost_shipping_price` (double, optional) — A fixed shipping cost for the variant. If defined, this value will be used during checkout instead of normal shipping-cost calculation.
- `purchasing_disabled` (boolean, optional) — If `true`, this variant will not be purchasable on the storefront.
- `purchasing_disabled_message` (string, optional) — If `purchasing_disabled` is `true`, this message should show on the storefront when the variant is selected.
- `upc` (string, optional, nullable) — The UPC code used in feeds for shopping comparison sites and external channel integrations.
- `inventory_level` (integer, optional) — Inventory level for the variant, which is used when the product’s inventory_tracking is set to `variant`. The Catalog API returns the inventory for only the default location. The inventory for a variant cannot exceed 2,147,483,647 in the catalog. The sum of the variant inventories, or the total inventory for a product, cannot exceed 2,147,483,647. If you exceed the limit, the store sets the variant inventory to the limit if no other variant inventories are set. If other variant inventories are set, the store does not save the variant inventory rather than setting the variant inventory to the remaining limit. The Catalog API handles limits in a different way than the Inventory API. For more information, see [Limit handling](/developer/docs/admin/catalog-and-inventory/inventory-adjustments#limit-handling-in-inventory-versus-catalog-api).
- `inventory_warning_level` (integer, optional) — When the variant hits this inventory level, it is considered low stock.
- `bin_picking_number` (string, optional) — Identifies where in a warehouse the variant is located.
- `id` (integer, optional)

### CatalogVariantsPutResponsesContentApplicationJsonSchemaDataItems

Common Variant properties.

- `cost_price` (double, optional) — The cost price of the variant. It is not affected by Price List prices. This value displays as null in the control panel when `cost_price` equals zero.
- `price` (double, optional) — This variant’s base price on the storefront. If a Price List ID is used, the Price List value will be used. If a Price List ID is not used, and this value is `null`, the product’s default price (set in the Product resource’s `price` field) will be used as the base price.
- `sale_price` (double, optional) — This variant’s sale price on the storefront. If a Price List ID is used, the Price List value will be used. If a Price List ID is not used, and this value is null, the product’s sale price (set in the Product resource’s `price` field) will be used as the sale price.
- `retail_price` (double, optional) — This variant’s retail price on the storefront. If a Price List ID is used, the Price List value will be used. If a Price List ID is not used, and this value is null, the product’s retail price (set in the Product resource’s `price` field) will be used as the retail price.
- `weight` (double, optional) — This variant’s base weight on the storefront. If this value is null, the product’s default weight (set in the Product resource’s weight field) will be used as the base weight.
- `width` (double, optional) — Width of the variant, which can be used when calculating shipping costs. If this value is `null`, the productʼs default width (set in the Product resourceʼs `width` field) will be used as the base width.
- `height` (double, optional) — Height of the variant, which can be used when calculating shipping costs. If this value is `null`, the productʼs default height (set in the Product resourceʼs `height` field) will be used as the base height.
- `depth` (double, optional) — Depth of the variant, which can be used when calculating shipping costs. If this value is `null`, the productʼs default depth (set in the Product resourceʼs `depth` field) will be used as the base depth.
- `is_free_shipping` (boolean, optional) — Flag used to indicate whether the variant has free shipping. If `true`, the shipping cost for the variant will be zero.
- `fixed_cost_shipping_price` (double, optional) — A fixed shipping cost for the variant. If defined, this value will be used during checkout instead of normal shipping-cost calculation.
- `purchasing_disabled` (boolean, optional) — If `true`, this variant will not be purchasable on the storefront.
- `purchasing_disabled_message` (string, optional) — If `purchasing_disabled` is `true`, this message should show on the storefront when the variant is selected.
- `upc` (string, optional, nullable) — The UPC code used in feeds for shopping comparison sites and external channel integrations.
- `inventory_level` (integer, optional) — Inventory level for the variant, which is used when the product’s inventory_tracking is set to `variant`. The Catalog API returns the inventory for only the default location. The inventory for a variant cannot exceed 2,147,483,647 in the catalog. The sum of the variant inventories, or the total inventory for a product, cannot exceed 2,147,483,647. If you exceed the limit, the store sets the variant inventory to the limit if no other variant inventories are set. If other variant inventories are set, the store does not save the variant inventory rather than setting the variant inventory to the remaining limit. The Catalog API handles limits in a different way than the Inventory API. For more information, see [Limit handling](/developer/docs/admin/catalog-and-inventory/inventory-adjustments#limit-handling-in-inventory-versus-catalog-api).
- `inventory_warning_level` (integer, optional) — When the variant hits this inventory level, it is considered low stock.
- `bin_picking_number` (string, optional) — Identifies where in a warehouse the variant is located.
- `id` (integer, optional)
- `product_id` (integer, optional)
- `sku` (string, optional)
- `sku_id` (integer, optional) — Read-only reference to v2 APIʼs SKU ID. Null if it is a base variant.
- `option_values` (list of CatalogVariantsPutResponsesContentApplicationJsonSchemaDataItemsOptionValuesItems, optional) — Array of option and option values IDs that make up this variant. Will be empty if the variant is the productʼs base variant.
- `calculated_price` (double, optional) — The price of the variant as seen on the storefront. This price takes into account `sale_price` and any price adjustment rules that are applicable to this variant.

### CatalogVariantsPutResponsesContentApplicationJsonSchemaMeta

Data about the response, including pagination and collection totals.

- `pagination` (CatalogVariantsPutResponsesContentApplicationJsonSchemaMetaPagination, optional) — Data about the response, including pagination and collection totals.

### CatalogVariantsPutResponsesContentApplicationJsonSchemaBatchErrorsItems

Error payload for the BigCommerce API.

- `status` (integer, optional) — The HTTP status code.
- `title` (string, optional) — The error title describing the particular error.
- `type` (string, optional)
- `instance` (string, optional)
- `errors` (CatalogVariantsPutResponsesContentApplicationJsonSchemaBatchErrorsItemsErrors, optional)

### CatalogVariantsPutResponsesContentApplicationJsonSchemaDataItemsOptionValuesItems

Common Option Value Product properties.

- `option_display_name` (string, optional) — The name of the option.
- `label` (string, optional) — The label of the option value.
- `id` (integer, optional)
- `option_id` (integer, optional)

### CatalogVariantsPutResponsesContentApplicationJsonSchemaMetaPagination

Data about the response, including pagination and collection totals.

- `total` (integer, optional) — Total number of items in the result set.
- `count` (integer, optional) — Total number of items in the collection response.
- `per_page` (integer, optional) — The amount of items returned in the collection per page, controlled by the limit parameter.
- `current_page` (integer, optional) — The page you are currently on within the collection.
- `total_pages` (integer, optional) — The total number of pages in the collection.
- `links` (CatalogVariantsPutResponsesContentApplicationJsonSchemaMetaPaginationLinks, optional) — Pagination links for the previous and next parts of the whole collection.

### CatalogVariantsPutResponsesContentApplicationJsonSchemaBatchErrorsItemsErrors

### CatalogVariantsPutResponsesContentApplicationJsonSchemaMetaPaginationLinks

Pagination links for the previous and next parts of the whole collection.

- `previous` (string, optional) — Link to the previous page returned in the response.
- `current` (string, optional) — Link to the current page returned in the response.
- `next` (string, optional) — Link to the next page returned in the response.

## Examples

**Request**

```json
[
  {}
]
```

**Response**

```json
{
  "data": [
    {
      "cost_price": 1.1,
      "price": 1.1,
      "sale_price": 1.1,
      "retail_price": 1.1,
      "weight": 1.1,
      "width": 1.1,
      "height": 1.1,
      "depth": 1.1,
      "is_free_shipping": true,
      "fixed_cost_shipping_price": 1.1,
      "purchasing_disabled": true,
      "purchasing_disabled_message": "string",
      "upc": "string",
      "inventory_level": 1,
      "inventory_warning_level": 1,
      "bin_picking_number": "string",
      "id": 1,
      "product_id": 1,
      "sku": "string",
      "sku_id": 1,
      "option_values": [
        {
          "option_display_name": "Color",
          "label": "Beige",
          "id": 1,
          "option_id": 1
        }
      ],
      "calculated_price": 1.1
    }
  ],
  "meta": {
    "pagination": {
      "total": 36,
      "count": 36,
      "per_page": 50,
      "current_page": 1,
      "total_pages": 1,
      "links": {
        "previous": "string",
        "current": "?page=1&limit=50",
        "next": "string"
      }
    }
  }
}
```

**SDK Code**

```python
import requests

url = "https://api.bigcommerce.com/stores/store_hash/v3/catalog/variants"

payload = [{}]
headers = {
    "Accept": "application/json",
    "X-Auth-Token": "<apiKey>",
    "Content-Type": "application/json"
}

response = requests.put(url, json=payload, headers=headers)

print(response.json())
```

```javascript
const url = 'https://api.bigcommerce.com/stores/store_hash/v3/catalog/variants';
const options = {
  method: 'PUT',
  headers: {
    Accept: 'application/json',
    'X-Auth-Token': '<apiKey>',
    'Content-Type': 'application/json'
  },
  body: '[{}]'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.bigcommerce.com/stores/store_hash/v3/catalog/variants"

	payload := strings.NewReader("[\n  {}\n]")

	req, _ := http.NewRequest("PUT", url, payload)

	req.Header.Add("Accept", "application/json")
	req.Header.Add("X-Auth-Token", "<apiKey>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.bigcommerce.com/stores/store_hash/v3/catalog/variants")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Put.new(url)
request["Accept"] = 'application/json'
request["X-Auth-Token"] = '<apiKey>'
request["Content-Type"] = 'application/json'
request.body = "[\n  {}\n]"

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.put("https://api.bigcommerce.com/stores/store_hash/v3/catalog/variants")
  .header("Accept", "application/json")
  .header("X-Auth-Token", "<apiKey>")
  .header("Content-Type", "application/json")
  .body("[\n  {}\n]")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('PUT', 'https://api.bigcommerce.com/stores/store_hash/v3/catalog/variants', [
  'body' => '[
  {}
]',
  'headers' => [
    'Accept' => 'application/json',
    'Content-Type' => 'application/json',
    'X-Auth-Token' => '<apiKey>',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://api.bigcommerce.com/stores/store_hash/v3/catalog/variants");
var request = new RestRequest(Method.PUT);
request.AddHeader("Accept", "application/json");
request.AddHeader("X-Auth-Token", "<apiKey>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "[\n  {}\n]", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = [
  "Accept": "application/json",
  "X-Auth-Token": "<apiKey>",
  "Content-Type": "application/json"
]
let parameters = [[]] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.bigcommerce.com/stores/store_hash/v3/catalog/variants")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "PUT"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```