---
title: "Shopify Image Automation: Badges & Overlays"
description: "Generate Shopify product images with sale badges, price overlays, and variant labels. Sync with your catalog for automatic updates. Full guide."
url: "https://www.imejis.io/blogs/integrations/shopify-product-image-automation"
published: "2026-02-24"
---

# Shopify Image Automation: Badges & Overlays

A Shopify store owner told me she spent every Sunday preparing product images for the week. New arrivals needed price tags. Sale items needed badges. Seasonal products needed themed overlays.

Eight hours. Every week. Just on images.

Her product data already existed in Shopify. Prices, sale status, inventory levels. All sitting there. She was manually transferring that data into Photoshop overlays.

That's exactly what automation fixes. Connect your Shopify catalog to an image API, and product images update themselves when your data changes. For a broader look at e-commerce image workflows, see [e-commerce product image automation](/blogs/use-cases/ecommerce-product-image-automation).

Here's how to build it.

## Why Automate Shopify Product Images?

Manual product image work doesn't scale. The math gets ugly fast:

| Catalog Size   | Images per Product | Manual Time/Month |
| -------------- | ------------------ | ----------------- |
| 100 products   | 3 images           | 15-20 hours       |
| 500 products   | 3 images           | 75-100 hours      |
| 1000+ products | 3 images           | You need a team   |

And that's just creation. What about when prices change? Sale periods? New variants?

**What automation handles:**

- Price overlays that update when prices change
- Sale badges that appear/disappear automatically
- "New" tags based on creation date
- "Low Stock" warnings based on inventory
- Variant-specific images (color, size labels)

One template, unlimited product images. Your catalog changes, your images follow. You can also generate [dynamic product images with pricing overlays](/blogs/use-cases/dynamic-product-images-pricing) for promotional campaigns.

## The Architecture

Shopify has webhooks for product events. We'll use those to trigger image generation.

```text
Shopify Product Update → Webhook → Image API → Storage → Back to Shopify (or CDN)
```

You have options for the middle layer:

1. **Shopify Flow + [Zapier](/blogs/zapier-integration-generate-dynamic-images)** - No code, easiest setup
2. **Custom App** - Most control, requires development
3. **External Cron** - Batch processing approach

I'll cover all three.

## Method 1: Shopify Flow + Zapier (No Code)

Shopify Flow is built-in automation for Shopify Plus. Combined with Zapier, it handles most use cases without code.

### Step 1: Set Up the Zap

**Trigger:** Webhooks by Zapier > Catch Hook

Copy the webhook URL. You'll use this in Shopify Flow.

**Action:** Code by Zapier (JavaScript)

```javascript
const response = await fetch("https://render.imejis.io/v1/YOUR_TEMPLATE_ID", {
  method: "POST",
  headers: {
    Authorization: "Bearer YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    product_name: inputData.title,
    price: inputData.price,
    compare_at_price: inputData.compare_at_price,
    product_image: inputData.image_url,
    show_sale_badge: inputData.compare_at_price ? "true" : "false",
  }),
})

// API returns image directly - we need to handle the binary
const imageBuffer = await response.arrayBuffer()

// For Zapier, we'll base64 encode it
output = {
  image_base64: Buffer.from(imageBuffer).toString("base64"),
}
```

**Action:** Upload to Cloudinary (or your CDN)

**Action:** Send webhook back to Shopify (or update via API)

### Step 2: Create Shopify Flow

In Shopify admin:

1. Go to Apps > Flow
2. Create workflow
3. Trigger: Product updated
4. Action: Send HTTP request to your Zapier webhook

Include product data in the request:

```json
{
  "title": "{{ product.title }}",
  "price": "{{ product.price }}",
  "compare_at_price": "{{ product.compare_at_price }}",
  "image_url": "{{ product.featured_image.src }}"
}
```

Now product updates trigger image regeneration automatically.

## Method 2: Custom Shopify App (Most Flexible)

For full control, build a custom app. This runs on your own server and handles everything.

### The App Structure

```text
shopify-image-app/
├── server.js          # Express server
├── webhooks/
│   └── product.js     # Webhook handler
├── services/
│   └── imageGen.js    # Imejis.io integration
└── package.json
```

### Webhook Handler

```javascript
// webhooks/product.js
import { generateProductImage } from "../services/imageGen.js"

export async function handleProductUpdate(req, res) {
  const product = req.body

  // Skip if no relevant changes
  if (!shouldRegenerateImage(product)) {
    return res.status(200).send("Skipped")
  }

  try {
    const imageUrl = await generateProductImage(product)

    // Optionally update product metafield with new image URL
    await updateProductMetafield(product.id, "generated_image", imageUrl)

    res.status(200).send("Generated")
  } catch (error) {
    console.error("Image generation failed:", error)
    res.status(500).send("Error")
  }
}

function shouldRegenerateImage(product) {
  // Logic to determine if image needs updating
  // Could check price changes, sale status, etc.
  return true
}
```

### Image Generation Service

```javascript
// services/imageGen.js
import fs from "fs"

export async function generateProductImage(product) {
  const response = await fetch(
    `https://render.imejis.io/v1/${process.env.TEMPLATE_ID}`,
    {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.IMEJIS_API_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        product_name: product.title,
        price: formatPrice(product.variants[0].price),
        original_price: product.variants[0].compare_at_price
          ? formatPrice(product.variants[0].compare_at_price)
          : null,
        product_image: product.images[0]?.src,
        is_on_sale: !!product.variants[0].compare_at_price,
        is_new: isNewProduct(product.created_at),
      }),
    }
  )

  // API returns image directly
  const imageBuffer = await response.arrayBuffer()

  // Upload to your CDN (Cloudinary, S3, etc.)
  const cdnUrl = await uploadToCDN(
    Buffer.from(imageBuffer),
    `product-${product.id}.png`
  )

  return cdnUrl
}

function formatPrice(price) {
  return `$${parseFloat(price).toFixed(2)}`
}

function isNewProduct(createdAt) {
  const thirtyDaysAgo = new Date()
  thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30)
  return new Date(createdAt) > thirtyDaysAgo
}
```

### Register Webhooks

Shopify's webhook topics and payload formats are documented in the [Shopify developer docs](https://shopify.dev), which is worth a look before you wire up the handler.

```javascript
// server.js

import { Shopify } from "@shopify/shopify-api"
import express from "express"

import { handleProductUpdate } from "./webhooks/product.js"

const app = express()

// Verify Shopify webhook signature
app.use("/webhooks", Shopify.Webhooks.Registry.process)

app.post("/webhooks/products/update", handleProductUpdate)

// Register webhooks on app install
Shopify.Webhooks.Registry.register({
  path: "/webhooks/products/update",
  topic: "PRODUCTS_UPDATE",
  webhookHandler: handleProductUpdate,
})
```

## Method 3: Batch Processing with Cron

For stores that don't need real-time updates, batch processing is simpler.

### The Script

```javascript
// scripts/sync-shopify-images.js
import Shopify from "shopify-api-node"

const shopify = new Shopify({
  shopName: process.env.SHOP_NAME,
  apiKey: process.env.SHOPIFY_API_KEY,
  password: process.env.SHOPIFY_PASSWORD,
})

async function processAllProducts() {
  let params = { limit: 50 }

  do {
    const products = await shopify.product.list(params)

    for (const product of products) {
      await generateAndStoreImage(product)
      // Rate limit
      await sleep(1000)
    }

    params = products.nextPageParameters
  } while (params)
}

async function generateAndStoreImage(product) {
  const needsImage = !product.metafields?.find(
    (m) => m.key === "generated_image"
  )

  if (!needsImage) return

  const response = await fetch(
    `https://render.imejis.io/v1/${process.env.TEMPLATE_ID}`,
    {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.IMEJIS_API_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        product_name: product.title,
        price: `$${product.variants[0].price}`,
        product_image: product.images[0]?.src,
      }),
    }
  )

  const imageBuffer = await response.arrayBuffer()
  const cdnUrl = await uploadToCDN(Buffer.from(imageBuffer))

  // Store URL in metafield
  await shopify.metafield.create({
    namespace: "generated",
    key: "product_image",
    value: cdnUrl,
    type: "url",
    owner_resource: "product",
    owner_id: product.id,
  })

  console.log(`Generated: ${product.title}`)
}

processAllProducts()
```

Run daily or weekly via cron or GitHub Actions. For more on processing large catalogs, see [batch image generation from CSV](/blogs/tutorials/batch-image-generation-csv).

## Template Design for E-commerce

Product image templates need to handle real-world data variations.

### Essential Elements

| Element        | Dynamic     | Notes                                  |
| -------------- | ----------- | -------------------------------------- |
| Product name   | Yes         | Handle long names (truncate or shrink) |
| Price          | Yes         | Include currency symbol                |
| Original price | Conditional | Show only if on sale (strikethrough)   |
| Sale badge     | Conditional | Show only when compare_at_price exists |
| Product photo  | Yes         | Pull from Shopify                      |
| "New" badge    | Conditional | Based on created_at date               |

### Handling Price Display

Your template logic should handle:

```json
{
  "price": "$79.99",
  "original_price": "$99.99", // null if not on sale
  "show_sale_badge": true,
  "discount_percent": "20%"
}
```

Calculate the discount in your code:

```javascript
const discount = originalPrice
  ? Math.round((1 - price / originalPrice) * 100)
  : 0
```

### Image Dimensions

| Platform        | Dimensions    | Use          |
| --------------- | ------------- | ------------ |
| Shopify product | 2048 x 2048px | Main listing |
| Social sharing  | 1200 x 630px  | FB/Twitter   |
| Instagram       | 1080 x 1080px | Social posts |
| Pinterest       | 1000 x 1500px | Pins         |

Create separate templates for each platform if needed.

## Displaying Generated Images

### Option 1: Product Metafields

Store the generated image URL in a product metafield, then display it in your theme:

```liquid
{% if product.metafields.generated.product_image %}
  <img src="{{ product.metafields.generated.product_image }}" alt="{{ product.title }}">
{% else %}
  <img src="{{ product.featured_image | img_url: 'large' }}" alt="{{ product.title }}">
{% endif %}
```

### Option 2: Separate Image Section

Add generated images as an additional product image via the API:

```javascript
await shopify.productImage.create(product.id, {
  src: cdnUrl,
  alt: `${product.title} - Social Image`,
  position: product.images.length + 1,
})
```

### Option 3: Social Meta Tags Only

If you only need images for social sharing, update your theme's `<head>`:

```liquid
{% if product.metafields.generated.og_image %}
  <meta property="og:image" content="{{ product.metafields.generated.og_image }}">
{% endif %}
```

## Sale Period Automation

Here's the real power: images that change with your sales.

### The Flow

1. You set a sale (compare_at_price) in Shopify
2. Webhook fires
3. Image regenerates with sale badge
4. Sale ends, you remove compare_at_price
5. Webhook fires
6. Image regenerates without badge

Zero manual image work for sales.

### Scheduled Sales

For scheduled sales, use Shopify Flow:

```text
Trigger: Scheduled time (sale start)
Action: Update product compare_at_price
→ This triggers your image generation webhook
```

And another flow for sale end.

## Cost Analysis

| Component         | Monthly Cost |
| ----------------- | ------------ |
| Shopify (Basic)   | $39          |
| Imejis.io Pro     | $24.99       |
| Cloudinary (Free) | $0           |
| **Added Cost**    | $24.99       |

For a 500-product store updating images weekly, that's about 5 cents per image. Compare to:

- Freelance designer: $15-50 per image
- Your time: Whatever you value it at

Check [Imejis.io pricing](/pricing) for exact rates. _Pricing reflects published rates as of July 2026; check the provider's site for current plans._

## Common Issues

### Images Look Pixelated

**Cause:** Source product images are low resolution.
**Fix:** Ensure your Shopify product images are at least 2048x2048px.

### Sale Badge Showing on Wrong Products

**Cause:** compare_at_price not cleared properly.
**Fix:** Audit your products for stale compare_at_price values.

### Webhook Timeout

**Cause:** Image generation takes too long.
**Fix:** Process async. Acknowledge webhook immediately, generate in background.

```javascript
app.post("/webhooks/products/update", (req, res) => {
  res.status(200).send("OK") // Respond immediately

  // Process in background
  setImmediate(() => {
    handleProductUpdate(req.body)
  })
})
```

## Getting Started

Your path:

1. **Design one product template** in [Imejis.io](https://www.imejis.io)
2. **Choose your method:** Flow+Zapier, custom app, or batch
3. **Test with 5 products** to verify the flow works
4. **Roll out to full catalog** gradually
5. **Set up sale automation** for hands-off promotions

Start with your best-selling products. Get the template right, then expand.

E-commerce success is about efficiency. Every hour you're not manually editing images is an hour you can spend on marketing, customer service, or product sourcing. For a roundup of the best tools in this space, see [best image APIs for e-commerce](/blogs/comparisons/best-image-apis-ecommerce).

[Start automating product images with Imejis.io](https://www.imejis.io)

## FAQ

### Will this slow down my Shopify store?

No. Generated images are stored on a CDN and load just like regular images. There's no runtime processing when customers view products.

### Can I use this with Shopify's free themes?

Yes. The automation works independently of your theme. You just need to add the code to display generated images (using metafields or product images).

### What happens during high-traffic sales events?

If you're using webhooks, queue your image generation to avoid overwhelming the API. For Black Friday-level traffic, pre-generate sale images before the event starts.

### Can I generate images for product variants?

Yes. Create a template that includes variant info (size, color) and generate separate images for each variant. You'll need to loop through variants in your code.

### How do I handle products without images?

Your template should have a fallback. Either use a placeholder image or skip generation for products without photos.
