DEV Community

Om Prakash
Om Prakash

Posted on

Step-by-Step: Automating Meesho Product Photos with Python and PixelAPI Virtual Try-On

If you're selling on Meesho, Myntra, or any Indian e-commerce platform, you know how expensive product photography gets. In this tutorial, I'll show you how to automate virtual try-on photo generation using Python and PixelAPI.

What you'll build: A Python script that takes a folder of garment images and automatically generates on-model photos for your catalog — at ₹3 per image instead of ₹500-2000 per shoot.


Step 1: Install & Authenticate

First, install the required library:

pip install requests
Enter fullscreen mode Exit fullscreen mode

Get your free API key from pixelapi.dev (first 50 images are free, no credit card needed).

import requests
import os
import time

API_KEY = "your_pixelapi_key_here"  # Get from pixelapi.dev
BASE_URL = "https://api.pixelapi.dev/v1"

headers = {
    "X-API-Key": API_KEY
}

# Test authentication
def test_auth():
    resp = requests.get(f"{BASE_URL}/account", headers=headers)
    if resp.status_code == 200:
        data = resp.json()
        print(f"✅ Authenticated! Credits remaining: {data['credits']}")
    else:
        print(f"❌ Auth failed: {resp.status_code}")
        print(resp.text)

test_auth()
Enter fullscreen mode Exit fullscreen mode

Step 2: Prepare Your Images

For best results:

Garment image tips:

  • Use a flat-lay or mannequin shot with plain background
  • JPEG or PNG, minimum 512×512 pixels
  • Good lighting, no shadows
  • Kurtas, sarees, shirts, dresses all work great

Person/model image tips:

  • Front-facing pose, arms slightly away from body
  • Plain or simple background
  • You can reuse the same model image for all garments!
  • Resolution: 512×768 or higher recommended
# Validate image before uploading
from pathlib import Path

def validate_image(image_path):
    path = Path(image_path)
    if not path.exists():
        raise FileNotFoundError(f"Image not found: {image_path}")
    if path.suffix.lower() not in ['.jpg', '.jpeg', '.png']:
        raise ValueError(f"Unsupported format: {path.suffix}")
    size = path.stat().st_size
    if size > 10 * 1024 * 1024:  # 10MB limit
        raise ValueError(f"Image too large: {size/1024/1024:.1f}MB (max 10MB)")
    print(f"{path.name}: {size/1024:.0f}KB — OK")

validate_image("model.jpg")
validate_image("kurta.jpg")
Enter fullscreen mode Exit fullscreen mode

Step 3: Submit Job and Poll for Result

def submit_tryon_job(person_image_path, garment_image_path, garment_type="upper"):
    """
    Submit a virtual try-on job.
    garment_type: 'upper' (shirts/kurtas), 'lower' (pants), or 'dress'
    """
    with open(person_image_path, 'rb') as person_f, \
         open(garment_image_path, 'rb') as garment_f:

        files = {
            'person_image': (os.path.basename(person_image_path), person_f, 'image/jpeg'),
            'garment_image': (os.path.basename(garment_image_path), garment_f, 'image/jpeg'),
        }
        data = {
            'garment_type': garment_type
        }

        resp = requests.post(
            f"{BASE_URL}/virtual-tryon",
            headers=headers,
            files=files,
            data=data
        )

    if resp.status_code == 202:
        job = resp.json()
        print(f"✅ Job submitted: {job['job_id']}")
        return job['job_id']
    else:
        print(f"❌ Submission failed: {resp.status_code}")
        print(resp.text)
        return None


def poll_job(job_id, timeout=120):
    """
    Poll until job completes. Returns result URL or None.
    """
    start = time.time()

    while time.time() - start < timeout:
        resp = requests.get(
            f"{BASE_URL}/jobs/{job_id}",
            headers=headers
        )

        if resp.status_code != 200:
            print(f"❌ Poll error: {resp.status_code}")
            return None

        job = resp.json()
        status = job['status']

        if status == 'completed':
            print(f"✅ Done! Result: {job['result_url']}")
            return job['result_url']
        elif status == 'failed':
            print(f"❌ Job failed: {job.get('error', 'Unknown error')}")
            return None
        else:
            elapsed = int(time.time() - start)
            print(f"⏳ Status: {status} ({elapsed}s elapsed)...")
            time.sleep(5)

    print(f"❌ Timeout after {timeout}s")
    return None


# Example usage
job_id = submit_tryon_job("model.jpg", "kurta.jpg", garment_type="upper")
if job_id:
    result_url = poll_job(job_id)
Enter fullscreen mode Exit fullscreen mode

Step 4: Save the Result Image

def save_result(result_url, output_path):
    """
    Download and save the result image.
    """
    resp = requests.get(result_url)

    if resp.status_code == 200:
        with open(output_path, 'wb') as f:
            f.write(resp.content)
        size = len(resp.content) / 1024
        print(f"✅ Saved: {output_path} ({size:.0f}KB)")
        return True
    else:
        print(f"❌ Download failed: {resp.status_code}")
        return False


# Complete single-image workflow
def process_single(person_image, garment_image, output_path, garment_type="upper"):
    job_id = submit_tryon_job(person_image, garment_image, garment_type)
    if not job_id:
        return False

    result_url = poll_job(job_id)
    if not result_url:
        return False

    return save_result(result_url, output_path)


# Test it!
process_single(
    person_image="model.jpg",
    garment_image="kurta.jpg",
    output_path="result_kurta.jpg",
    garment_type="upper"
)
Enter fullscreen mode Exit fullscreen mode

Step 5: Batch Process a Folder of Garment Images

This is where it gets powerful. Automate your entire catalog:

import os
import glob
from pathlib import Path
import time


def batch_process_catalog(
    person_image,
    garments_folder,
    output_folder,
    garment_type="upper",
    delay_between=2  # seconds between jobs (be nice to the API)
):
    """
    Process all garment images in a folder.

    Args:
        person_image: Path to your model photo (reused for all garments)
        garments_folder: Folder containing garment images
        output_folder: Where to save results
        garment_type: 'upper', 'lower', or 'dress'
        delay_between: Seconds to wait between submissions
    """
    os.makedirs(output_folder, exist_ok=True)

    # Find all garment images
    patterns = ['*.jpg', '*.jpeg', '*.png', '*.JPG', '*.JPEG', '*.PNG']
    garment_files = []
    for pattern in patterns:
        garment_files.extend(glob.glob(os.path.join(garments_folder, pattern)))

    garment_files.sort()
    total = len(garment_files)

    print(f"\n🚀 Starting batch: {total} garments")
    print(f"📁 Output: {output_folder}")
    print(f"💰 Estimated cost: ₹{total * 3} ({total} images × ₹3)\n")

    results = {
        'success': [],
        'failed': [],
        'skipped': []
    }

    for i, garment_path in enumerate(garment_files, 1):
        garment_name = Path(garment_path).stem
        output_path = os.path.join(output_folder, f"{garment_name}_model.jpg")

        # Skip if already processed
        if os.path.exists(output_path):
            print(f"[{i}/{total}] ⏭️  Skipping {garment_name} (already done)")
            results['skipped'].append(garment_name)
            continue

        print(f"\n[{i}/{total}] Processing: {garment_name}")

        try:
            success = process_single(
                person_image=person_image,
                garment_image=garment_path,
                output_path=output_path,
                garment_type=garment_type
            )

            if success:
                results['success'].append(garment_name)
                print(f"{garment_name}{output_path}")
            else:
                results['failed'].append(garment_name)
                print(f"{garment_name} failed")

        except Exception as e:
            print(f"  ❌ Error processing {garment_name}: {e}")
            results['failed'].append(garment_name)

        # Rate limit
        if i < total:
            time.sleep(delay_between)

    # Summary
    print(f"\n{'='*50}")
    print(f"✅ Success: {len(results['success'])}")
    print(f"❌ Failed:  {len(results['failed'])}")
    print(f"⏭️  Skipped: {len(results['skipped'])}")
    print(f"Total cost: ₹{len(results['success']) * 3}")

    if results['failed']:
        print(f"\nFailed items: {', '.join(results['failed'])}")

    return results


# Run it! Put all your kurta/saree photos in 'my_garments/' folder
results = batch_process_catalog(
    person_image="model.jpg",        # Your model photo
    garments_folder="my_garments/",  # Folder with kurta/saree images
    output_folder="catalog_ready/",  # Where results go
    garment_type="upper",            # 'upper', 'lower', or 'dress'
)
Enter fullscreen mode Exit fullscreen mode

Expected output:

🚀 Starting batch: 50 garments
📁 Output: catalog_ready/
💰 Estimated cost: ₹150 (50 images × ₹3)

[1/50] Processing: kurta_red_001
✅ Job submitted: job_abc123
⏳ Status: processing (5s elapsed)...
✅ Done! Result: https://cdn.pixelapi.dev/results/...
✅ Saved: catalog_ready/kurta_red_001_model.jpg (245KB)
  ✅ kurta_red_001 → catalog_ready/kurta_red_001_model.jpg
...
==================================================
✅ Success: 50
❌ Failed:  0
⏭️  Skipped: 0
Total cost: ₹150
Enter fullscreen mode Exit fullscreen mode

Error Handling Tips

# Retry logic for failed jobs
def process_with_retry(person_image, garment_image, output_path, 
                       garment_type="upper", max_retries=3):
    for attempt in range(max_retries):
        try:
            success = process_single(person_image, garment_image, 
                                     output_path, garment_type)
            if success:
                return True
            print(f"Attempt {attempt+1} failed, retrying...")
            time.sleep(10 * (attempt + 1))  # Exponential backoff
        except Exception as e:
            print(f"Attempt {attempt+1} error: {e}")
            time.sleep(10)
    return False
Enter fullscreen mode Exit fullscreen mode

What This Saves You

Method Cost per image Time Quality
Real model shoot ₹500-2000 2-7 days High
Freelance editor ₹100-300 1-2 days Medium
PixelAPI AI ₹3 10 seconds High

For a Meesho seller with 200 SKUs:

  • Traditional: ₹1,00,000+ per season
  • PixelAPI: ₹600 per season

Savings: ₹99,400+ 💰


Next Steps

  1. Sign up free at pixelapi.dev — 50 free images, no credit card
  2. Read the full docs with curl and Node.js examples
  3. Run the batch script on your garment folder
  4. Upload to Meesho and watch your conversions improve!

Full tutorial with curl + Node.js examples: https://pixelapi.dev/tutorials/virtual-try-on.html

Top comments (0)