# Set the product image on BigCommerce with the v3 API
> Add a product image on BigCommerce with the v3 Catalog API using the URL SocialCutter returns and mark it as the main one. curl and Python.
- URL: https://socialcutter.theboomer.dev/en/guides/bigcommerce/
- Idioma: en
- Familia: cms
- Actualizado: 2026-09-24
- Palabras clave: BigCommerce, Catalog API, product image, API v3, image_url, is_thumbnail, OAuth scopes, Python
## Why generate the sizes before uploading

A product image shows up in the catalogue grid, on the product page, in the cart line and in the social posts that promote the product. Each slot wants a different ratio, and uploading one copy per slot leaves the catalogue full of near-identical files.

The flow is: **one master goes in, SocialCutter returns one output per destination, and BigCommerce receives the right one in each slot**. The `cover` fit mode (the default) is a **centred crop**: it scales the image and splits the excess evenly on both sides. There is no subject detection and no automatic step that decides what to crop.

| Use in BigCommerce | SocialCutter destination | Size |
|---|---|---|
| Product main image | `instagram` `post` | 1080x1080 (1:1) |
| Second product image | `instagram` `story` | 1080x1920 (9:16) |
| Category banner | `facebook` `post` | 1200x630 (1.91:1) |
| Store header | `twitter` `header` | 1500x500 (3:1) |
| Product video thumbnail | `youtube` `thumbnail` | 1280x720 (16:9) |

The full catalogue comes from `GET /api/v1/platforms`, which is public, and is summarised in the [social media sizes guide](/en/guides/medidas-redes-sociales/).

> Note: there is no 4:5 in the SocialCutter catalogue. Vertical is 9:16 (1080x1920) and square is 1:1 (1080x1080). Use 1:1 as the main image; if you need an exact 4:5, crop outside SocialCutter.

## Before you start: credential and scopes

A BigCommerce API account is created in the control panel under **Settings → API → API accounts**. You pick the scope as you create it. Since every request in this guide hits the Catalog API v3, you need the products scope:

| Scope | What you need it for here |
|---|---|
| `store_v2_products` | Creating and updating product images |
| `store_v2_products_read_only` | Only if you just read the catalogue |

Scopes are granted when the credential is created and are not widened per request: if one is missing, regenerate the account. The current list and names live at https://developer.bigcommerce.com/docs/start/authentication/api-accounts — check it, because BigCommerce has been consolidating endpoints under a shared products scope.

Authentication uses two headers: `X-Auth-Token` with the access token and `Accept: application/json`. The store hash goes in the path:

```bash
export BC_STORE="your_store_hash"
export BC_TOKEN="your_access_token"
export SC_KEY="sc_your_key"
export BC_API="https://api.bigcommerce.com/stores/$BC_STORE/v3"
```

```bash
curl -s "$BC_API/catalog/products?limit=1" \
  -H "X-Auth-Token: $BC_TOKEN" \
  -H "Accept: application/json" | jq '.data[0] | {id, name}'
```

## 1. Process the master with SocialCutter

```bash
curl -s -X POST "https://api.socialcutter.theboomer.dev/api/v1/images/process" \
  -H "X-API-Key: *** \
  -H "Content-Type: application/json" \
  -d '{
    "source": { "type": "url", "value": "https://your-cdn.com/master.jpg" },
    "destinations": [
      { "platform": "instagram", "format": "post" },
      { "platform": "instagram", "format": "story" }
    ]
  }' > sc.json

jq '.image_id, (.outputs[] | {url, platform, format, width, height})' sc.json
```

Each output is a public URL. BigCommerce accepts URLs when creating an image, so there is nothing to download.

## 2. Create the product image

The creation endpoint lives under the product and takes **one image per request**. It has two mutually exclusive modes:

- `image_url` in JSON: you pass the SocialCutter URL and BigCommerce fetches it.
- `image_file` in multipart: you upload the binary. The header must then be `multipart/form-data`.

```bash
MAIN_URL=$(jq -r '.outputs[] | select(.platform=="instagram" and .format=="post") | .url' sc.json)

curl -s -X POST "$BC_API/catalog/products/123/images" \
  -H "X-Auth-Token: $BC_TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{\"image_url\": \"$MAIN_URL\", \"is_thumbnail\": true, \"description\": \"Front view 1:1\"}" \
  > image.json

jq '.data | {id, is_thumbnail, url_standard, url_thumbnail}' image.json
```

With `is_thumbnail: true` at creation the image is born as the main one. `description` is the alt text the storefront uses.

Reference: https://developer.bigcommerce.com/docs/store-operations/catalog

## 3. Python variant and multipart variant

With `requests` the flow is the same. The JSON goes through `json=` and the multipart through `files=`; mixing `image_url` with `image_file` is an error.

```python
import os
import requests

SC_KEY = os.environ["SC_KEY"]
BC_API = f'https://api.bigcommerce.com/stores/{os.environ["BC_STORE"]}/v3'
BC_HEADERS = {
    "X-Auth-Token": os.environ["BC_TOKEN"],
    "Accept": "application/json",
}

sc = requests.post(
    "https://api.socialcutter.theboomer.dev/api/v1/images/process",
    headers={"X-API-Key": SC_KEY, "Content-Type": "application/json"},
    json={
        "source": {"type": "url", "value": "https://your-cdn.com/master.jpg"},
        "destinations": [{"platform": "instagram", "format": "post"}],
    },
    timeout=30,
)
sc.raise_for_status()
main_url = sc.json()["outputs"][0]["url"]

product_id = 123
created = requests.post(
    f"{BC_API}/catalog/products/{product_id}/images",
    headers=BC_HEADERS,
    json={"image_url": main_url, "is_thumbnail": True, "description": "Front view 1:1"},
    timeout=30,
)
created.raise_for_status()
image = created.json()["data"]
print(image["id"], image["is_thumbnail"], image["url_standard"])

# Multipart variant, when the image only exists on disk:
with open("story.jpg", "rb") as fh:
    up = requests.post(
        f"{BC_API}/catalog/products/{product_id}/images",
        headers=BC_HEADERS,          # requests sets the multipart Content-Type itself
        files={"image_file": ("story.jpg", fh, "image/jpeg")},
        data={"is_thumbnail": "false"},
        timeout=60,
    )
up.raise_for_status()
```

Form fields carry no types: `is_thumbnail` travels as the string `"false"` or `"true"`.

## 4. Changing the main image afterwards

If the image already exists and you want it promoted to main, update it by its id. A product can only have one thumbnail at a time, so the previous one stops being it implicitly.

```bash
IMAGE_ID=$(jq -r '.data.id' image.json)

curl -s -X PUT "$BC_API/catalog/products/123/images/$IMAGE_ID" \
  -H "X-Auth-Token: $BC_TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{"is_thumbnail": true}' | jq '.data.is_thumbnail'
```

To change the order the images are shown in, use `sort_order` (higher numbers lose priority). Endpoint reference: https://developer.bigcommerce.com/docs/store-operations/catalog

## Cost

- **1 use per destination** (platform and format) per request; duplicates are not charged twice.
- Failed processings are refunded.
- Plans 0/3/9/29 EUR, all with API and MCP.

## Common errors

| Symptom | Cause | Fix |
|---|---|---|
| `401` from BigCommerce | Access token missing or from another store | Check `X-Auth-Token` and that the store hash in the path is the right one |
| `403` from BigCommerce | The credential lacks the products scope | Regenerate the API account with `store_v2_products` |
| `422` with `image_url` | The URL is not public, exceeds 255 characters, or the format is unsupported | Pass the SocialCutter URL and use JPEG, PNG, GIF, WEBP, BMP, WBMP or XBM |
| `413`/image rejected | The file is over 8 MB | Generate a smaller master with SocialCutter and retry |
| `400` when sending both fields | `image_url` and `image_file` were sent together | Pick one: JSON with a URL or multipart with a file |
| `413` from SocialCutter | The master is over 5 MB | Shrink the master before processing it |

## No code and next steps

An automation tool chains the same steps with nodes: a trigger, an HTTP node to `/api/v1/images/process` and an HTTP node to the Catalog API with the output URL. The general pattern is in the [automating social media images guide](/en/guides/automatizar-imagenes-redes-sociales/).

- Shopify: [Integrate SocialCutter with the Shopify Admin API](/en/guides/shopify/)
- WordPress and WooCommerce: [Integrate SocialCutter with WordPress and WooCommerce](/en/guides/wordpress/)
- Python: [Process images with the API from Python](/en/guides/python/)
- Terminal: [Process images with the API from the terminal (curl)](/en/guides/curl/)
- Documentation: https://docs.socialcutter.theboomer.dev