Wedding Batch Studio, from sign-in to delivery ZIP
Follow the exact browser workflow a photographer sees, then build the same tested project lifecycle in Python. The video and screenshots show real processing and real output files.
Live contract verified · 29 checks · 23 August 2026Before you start
- A PixelAPI account and API key from the dashboard.
- Your export-ready photographs in JPEG, PNG, or WebP format.
- Optional: one approved reference image if you want Reference Match.
- Each file must be 40 MB or smaller. A project can contain up to 5,000 source photos.
- Processing costs one credit per selected source photo. The reference does not count as a source.
Part 1: use the browser
Open pixelapi.dev/app and use the normal PixelAPI sign-in. Once the dashboard opens, choose Wedding Batch Studio under Studios in the left menu.
Name the gallery and click Create project, or choose a saved project. Add JPEG, PNG or WebP photos. Reselect the same files after an interrupted upload; confirmed uploads are skipped.
Click Analyze gallery for focus, exposure and duplicate suggestions. Use Keep and star ratings to choose your set. Keep suggested duplicates selects one recommendation per group; check meaningful moments before accepting a suggestion.
Choose True Color, Natural Warm, Cinematic Soft or Reference Match. Adjust exposure, warmth, contrast, saturation and strength. Reference Match needs a reference upload. Click a photo or Preview selected photo for the free before/after slider. Original faces are preserved; eye checks are advisory and do not generate open eyes.
The button shows the exact selected count and credit cost. Click Finish selected when satisfied. Processing continues if you leave the page. Selection and the recipe are locked for this run.
Reopen any finished photo to compare it with its saved original. Download the finished ZIP. Retry failed photos only; failed and unprocessed photos are refunded.
Part 2: review the output and deliver it
Do not jump straight from 100% to client delivery. Open representative images from every lighting group—getting ready, ceremony, portraits, reception, and dance floor. Batch finishing is a base pass; emotionally important or difficult frames may still deserve individual work.


When the review is clean, download the project ZIP. It contains a manifest.json and a finished/ folder with ordered JPEG outputs. The manifest maps stable source identities to output URLs and statuses.
Part 3: automate the same workflow in Python
Install Requests, put your real key in an environment variable, and keep the deliberately non-working placeholder in source control.
python -m pip install requests
# macOS or Linux
export PIXELAPI_API_KEY="pxapi_your_key_here"
# PowerShell
$env:PIXELAPI_API_KEY = "pxapi_your_key_here"
Save this as wedding_batch.py. Put an approved reference plus your selected JPEGs inside wedding_photos/.
import os
import time
import zipfile
from pathlib import Path
import requests
BASE = "https://api.pixelapi.dev"
API_KEY = os.getenv("PIXELAPI_API_KEY", "pxapi_demo_not_a_real_key")
PHOTOS = Path("wedding_photos")
REFERENCE = PHOTOS / "approved_reference.jpg"
OUTPUT = Path("wedding_delivery")
session = requests.Session()
session.headers.update({"Authorization": f"Bearer {API_KEY}"})
def api(method, path, **kwargs):
url = path if path.startswith("http") else f"{BASE}{path}"
response = session.request(method, url, timeout=120, **kwargs)
response.raise_for_status()
return response
# Reuse this key if your create request must be retried.
project = api("POST", "/v1/wedding-projects", json={
"name": "Ananya and Rohan - final gallery",
"preset": "reference_match",
"workflow_version": 2,
"idempotency_key": "ananya-rohan-final-v1",
"preserve_resolution": True,
"jpeg_quality": 94,
}).json()
project_id = project["project_id"]
with REFERENCE.open("rb") as handle:
api("POST", f"/v1/wedding-projects/{project_id}/assets",
files={"file": (REFERENCE.name, handle, "image/jpeg")},
data={"client_asset_id": "approved-reference",
"sort_order": 0, "role": "reference"})
sources = sorted(p for p in PHOTOS.glob("*.jpg") if p != REFERENCE)
for index, photo in enumerate(sources):
with photo.open("rb") as handle:
api("POST", f"/v1/wedding-projects/{project_id}/assets",
files={"file": (photo.name, handle, "image/jpeg")},
data={"client_asset_id": f"source-{index:05d}",
"sort_order": index, "role": "source"})
print(f"uploaded {index + 1}/{len(sources)}: {photo.name}")
api("POST", f"/v1/wedding-projects/{project_id}/start", json={})
while True:
project = api("GET", f"/v1/wedding-projects/{project_id}").json()
counts = project["counts"]
print(project["progress_percent"], counts)
if project["status"] in {
"completed", "completed_with_errors", "failed", "cancelled"
}:
break
time.sleep(2)
if project["status"] not in {"completed", "completed_with_errors"}:
raise SystemExit(f"Project ended as {project['status']}")
OUTPUT.mkdir(exist_ok=True)
zip_path = OUTPUT / "wedding-delivery.zip"
zip_path.write_bytes(api("GET", project["delivery_url"]).content)
with zipfile.ZipFile(zip_path) as archive:
archive.extractall(OUTPUT / "finished")
manifest = api(
"GET", f"/v1/wedding-projects/{project_id}/manifest"
).json()
print(f"finished {manifest['project']['counts']['completed']} photos")
Resume, cancel, and retry
| Need | Request | Behavior |
|---|---|---|
| Find recent projects | GET /v1/wedding-projects?limit=20 | Returns only projects owned by the current account. |
| Resume state | GET /v1/wedding-projects/{id} | Returns progress, counts, and delivery URLs. |
| List failed files | GET /v1/wedding-projects/{id}/assets?status=failed | Paginate up to 500 assets per page. |
| Retry all failures | POST /v1/wedding-projects/{id}/retry with {} | Queues only failed source assets after a run finishes. |
| Retry selected failures | POST /v1/wedding-projects/{id}/retry | Send {"asset_ids":["uuid"]}. |
| Cancel | POST /v1/wedding-projects/{id}/cancel | A draft cancels immediately; an active job stops safely between assets. |
Common responses
- 401/403: missing, expired, or invalid API key.
- 402: the account needs enough credits for every selected source image when starting.
- 409: missing source images, missing Reference Match image, upload attempted after start, retry requested before completion, or no failed images exist.
- 413: a file is over 40 MB or the project already has 5,000 source images.
- 422: invalid preset, JPEG quality outside 80–98, unsupported image, or malformed asset ID.
For the complete request and response fields, use the Wedding Projects API reference.