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 culled, 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 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.
Use the couple, date, or your internal job number. A specific name is useful when you resume later or keep several weddings active.
Natural Warm gently warms the gallery. True Color keeps the treatment restrained. Cinematic Soft adds a softer, slightly moodier finish. Reference Match uses the color character of one approved reference photograph.
When Reference Match is selected, choose the approved reference first. The studio will not start a Reference Match project without it.
Choose the culled gallery. The browser uploads files sequentially instead of holding the entire batch in memory. Each file receives a stable identity, so the same upload can be resumed without duplicates.
Confirm the source count, then click Start processing. The project shows total, completed, remaining, failed, and cancelled counts plus overall progress.
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",
"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 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.