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
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()
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")
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)
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"
)
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'
)
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
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
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
- Sign up free at pixelapi.dev — 50 free images, no credit card
- Read the full docs with curl and Node.js examples
- Run the batch script on your garment folder
- 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)