API

Shopify: Give Every Product Photo a White Background Automatically (Admin API + Background Removal API)

September 6, 20267 min readBy BG Clear Editorial

Consistent white backgrounds lift conversion and are mandatory the moment you syndicate to Google Shopping, Amazon or Meta catalogues. Editing 800 SKUs by hand is not a plan. This is the automated version: a Node script that reads your product images through the Shopify Admin GraphQL API, sends each one to the bgclear API with a white fill, and attaches the result back to the product. It runs from your laptop or a cron; no Shopify app install required.

In this guide

What you need

A Shopify custom app (Settings → Apps and sales channels → Develop apps) with the read_products and write_products scopes and its Admin API access token; a bgclear API key from the dashboard (10 free full-resolution credits to start; preview size is free for a dry run); Node 18+. Shopify's productCreateMedia mutation accepts a public URL as originalSource, and the bgclear JSON response gives you exactly that — a hosted result URL valid for 24 hours — so no file juggling.

Step 1: list products and images

const SHOP = "your-store.myshopify.com";
const ADMIN = `https://${SHOP}/admin/api/2025-07/graphql.json`;
const shopify = async (query, variables = {}) => {
  const res = await fetch(ADMIN, {
    method: "POST",
    headers: { "Content-Type": "application/json", "X-Shopify-Access-Token": process.env.SHOPIFY_TOKEN },
    body: JSON.stringify({ query, variables }),
  });
  const { data, errors } = await res.json();
  if (errors) throw new Error(JSON.stringify(errors));
  return data;
};

async function* products() {
  let cursor = null;
  do {
    const data = await shopify(`query($cursor: String) {
      products(first: 50, after: $cursor) {
        pageInfo { hasNextPage endCursor }
        nodes { id title media(first: 10) { nodes { id ... on MediaImage { image { url } } } } }
      }
    }`, { cursor });
    for (const p of data.products.nodes) yield p;
    cursor = data.products.pageInfo.hasNextPage ? data.products.pageInfo.endCursor : null;
  } while (cursor);
}

Step 2: remove the background with a white fill

JSON mode: pass the Shopify CDN URL as image_url, ask for bg_color=ffffff and format=jpg (Shopify re-encodes anyway, and JPG on white is the smallest), and use the product-image id as the idempotency key so a re-run never charges twice.

async function whiteBackground(imageUrl, idemKey) {
  const res = await fetch("https://www.bgclear.ai/api/v1/remove", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.BGCLEAR_API_KEY}`,
      Accept: "application/json",
      "Content-Type": "application/json",
      "Idempotency-Key": idemKey,
    },
    body: JSON.stringify({ image_url: imageUrl, size: "auto", format: "jpg", bg_color: "ffffff" }),
  });
  if (res.status === 429) {
    await new Promise((r) => setTimeout(r, 1000 * Number(res.headers.get("X-RateLimit-Reset") ?? 5)));
    return whiteBackground(imageUrl, idemKey);
  }
  if (!res.ok) throw new Error(`bgclear ${res.status}: ${(await res.json()).error.message}`);
  return res.json();   // { url, width, height, credits_remaining, ... }
}

size: "auto" processes images up to 4 megapixels at full resolution synchronously — most Shopify product images are 2048 px and fit. For larger originals, post to /api/v1/jobs instead (bulk guide).

Step 3: attach the result to the product

async function attach(productId, url, alt) {
  const data = await shopify(`mutation($id: ID!, $media: [CreateMediaInput!]!) {
    productCreateMedia(productId: $id, media: $media) {
      media { id status }
      mediaUserErrors { field message }
    }
  }`, { id: productId, media: [{ originalSource: url, mediaContentType: "IMAGE", alt }] });
  const errs = data.productCreateMedia.mediaUserErrors;
  if (errs.length) throw new Error(errs.map((e) => e.message).join("; "));
  return data.productCreateMedia.media[0];
}

for await (const p of products()) {
  const first = p.media.nodes.find((m) => m.image);
  if (!first) continue;
  const key = `shopify-${first.id}`;
  const { url, credits_remaining } = await whiteBackground(first.image.url, key);
  await attach(p.id, url, `${p.title} on white background`);
  console.log(p.title, "→ attached; credits left", credits_remaining);
}

Shopify fetches the image from the result URL asynchronously (status: "UPLOADED" then "READY"), well inside the 24-hour window. Reorder or delete the old media afterwards with productReorderMedia / productDeleteMedia once you have eyeballed a sample — keep the originals until then.

Dry run, cost and re-runs

Dry run first with size: "preview": it is free, returns an 800 px result, and lets you check edges on jewellery, glass and white products before spending a credit. A full run costs one credit per image — 500 SKUs is the $39 pack ($0.078 per image), 2,000 is $129. Credits never expire, so buy once for the catalogue. Because the idempotency key is the Shopify media id, re-running the script after a crash skips the charge for images already processed within 24 hours; beyond that, track processed ids in a JSON file so the loop can resume. If you sell on Amazon or Flipkart as well, the same JPG-on-white output meets their main-image requirement — see Amazon listing background remover.

Frequently asked questions

Do I need to install a Shopify app?

No — a custom app token with read_products and write_products is enough, and the script runs anywhere Node runs.

Will Shopify accept the temporary result URL?

Yes. productCreateMedia downloads from originalSource when you call it; the bgclear result URL stays valid for 24 hours, far longer than needed.

Does the white match Shopify's or Amazon's requirement?

bg_color=ffffff produces pure RGB 255,255,255, which is what marketplace main-image rules ask for. Keep a subtle drop shadow out of the main image.

What about variant images and Shopify Plus stores with thousands of products?

The same loop works over variant media; for very large catalogues submit 50 image_urls per call to POST /api/v1/jobs/batch with a callback URL instead of the synchronous endpoint.

Ship it with the bgclear API

remove.bg-compatible endpoints, credits from $9 for 100 images, no subscription. Pay, get a key, make your first call in under two minutes.

Get API credits →

Tools for this guide

How to Get White Background in Product Photos

how to get white background in product photos

Shopify Product Photo Background Remover

shopify product photo background remover

White Background Product Photo Online

white background product photo online

Keep reading

API

Remove Image Backgrounds in Node.js with fetch and FormData (No SDK)

Node 18+ built-in fetch, a file upload, a URL input, an Express proxy endpoint so your browser never sees the key, and retries that cannot double-charge.

API

Bulk Background Removal via API: Async Jobs, Batches of 50 and Webhooks

How to process a catalogue of thousands of images without babysitting a script: batch submission, callbacks instead of polling, credit accounting, and what to do when the queue is full.

API

Automate Background Removal in n8n, Zapier and Make (No Code)

Exact node-by-node settings to remove backgrounds inside an automation: HTTP Request node in n8n, Webhooks by Zapier, and Make's HTTP module — then send the cutout to Drive, Shopify or Airtable.

API

Best Background Remover APIs for Developers

A working person's walk-through of background remover api for integrating background removal. Five-second cutouts, full resolution, no watermark.