A REST API that builds a photo mosaic from a set of photos: you send one main photo and the photos for the tiles, we assemble a mosaic where every tile is one of those photos. You get a watermarked preview first and unlock the HD file for credits.
The same engine has built mosaics for Collager.ai customers, print shops and events. One main photo plus a few hundred personal photos make one picture.

main_image_url — the picture to recreate
tile_image_urlsWant to see the photos fly into place? Watch the photo mosaic assembly video — we can render one for any mosaic.
Assembly takes minutes, not milliseconds, so the API is asynchronous: create a job, then poll it or wait for a webhook.
POST /v1/mosaicsSend the main photo and the tile photos — as a list of URLs, one ZIP, a folder in your cloud bucket, a file upload, or an upload link for your own users (six ways). Optionally a webhook_url. You get back a job id straight away.
GET /v1/mosaics/{id}Check the status: queued → processing → preview_ready (or failed with a reason).
When the status is preview_ready, the response carries preview_url — a watermarked image you can show to your user for approval.
POST /v1/mosaics/{id}/unlockSpend credits from your Collager.ai balance to unlock the mosaic. The response carries hd_url — the clean full-resolution file.
If you passed webhook_url, we POST the job object to it when the preview is ready or the job fails — no polling needed.
Authentication is a bearer key: Authorization: Bearer <your key>. Base URL: https://api.collager.ai/v1 (planned).
# 1. Create a mosaic job curl -X POST https://api.collager.ai/v1/mosaics \ -H "Authorization: Bearer $COLLAGER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "main_image_url": "https://example.com/portrait.jpg", "tile_image_urls": ["https://example.com/p/001.jpg", "https://example.com/p/002.jpg"], "webhook_url": "https://example.com/hooks/collager" }' # → 202 {"id": "msc_7Hq2...", "status": "queued"} # 2. Poll the status curl https://api.collager.ai/v1/mosaics/msc_7Hq2... -H "Authorization: Bearer $COLLAGER_API_KEY" # → {"id": "msc_7Hq2...", "status": "preview_ready", "preview_url": "https://..."} # 3. Unlock the HD file for credits curl -X POST https://api.collager.ai/v1/mosaics/msc_7Hq2.../unlock -H "Authorization: Bearer $COLLAGER_API_KEY" # → {"id": "msc_7Hq2...", "status": "unlocked", "hd_url": "https://...", "credits_left": 41}
import os, time, requests API = "https://api.collager.ai/v1" H = {"Authorization": f"Bearer {os.environ['COLLAGER_API_KEY']}"} job = requests.post(f"{API}/mosaics", headers=H, json={ "main_image_url": "https://example.com/portrait.jpg", "tile_image_urls": [f"https://example.com/p/{i:03}.jpg" for i in range(1, 301)], }).json() while job["status"] in ("queued", "processing"): time.sleep(20) # assembly takes minutes job = requests.get(f"{API}/mosaics/{job['id']}", headers=H).json() if job["status"] == "preview_ready": print("Preview:", job["preview_url"]) # watermarked hd = requests.post(f"{API}/mosaics/{job['id']}/unlock", headers=H).json() print("HD file:", hd["hd_url"])
POST https://example.com/hooks/collager { "event": "mosaic.preview_ready", "mosaic": { "id": "msc_7Hq2...", "status": "preview_ready", "preview_url": "https://...", "tiles_used": 300, "created_at": "2026-10-14T12:00:00Z" } } # Other events: mosaic.unlocked, mosaic.failed. Each delivery is signed with an X-Collager-Signature header.
The full draft is in openapi.yaml (OpenAPI 3.1, marked x-status: early-access).
| Endpoint | What it does |
|---|---|
POST/v1/mosaics | Create a mosaic job from a main photo and tile photos. Returns the job id. |
GET/v1/mosaics/{id} | Job status; preview_url when ready; failure reason if any. |
POST/v1/mosaics/{id}/start | Start assembly of a job that was collecting uploads from your users. |
POST/v1/mosaics/{id}/unlock | Spend credits and get hd_url, the clean full-resolution file. |
GET/v1/account | Credit balance, key type and limits. |
The details an integration needs. Everything here is the planned v1 behaviour — the same contract as in openapi.yaml.
Every request carries Authorization: Bearer <key> over HTTPS. A key is bound to one Collager.ai account and spends that account's credits.
Two kinds of keys: ck_live_… creates real jobs and can unlock HD files; ck_test_… creates jobs that end at a watermarked preview and never spend credits. Keep keys on your server — never in a browser or mobile app.
| Field | Type | Meaning |
|---|---|---|
main_image_url | URL, required | The picture to recreate. JPEG or PNG, publicly reachable or a pre-signed link. A face or a clear subject works best. |
tile_image_urls | array of URLs | Photos for the tiles, 30 to 10,000. A few hundred give a good result; with fewer photos tiles repeat. |
tiles_zip_url | URL | Or: one ZIP archive with all tile photos. |
tiles_source | object | Or: a cloud folder we read from — {"type": "s3", "bucket": "…", "prefix": "event-42/", "region": "eu-west-1"} (also gcs, azure). |
collect_uploads | boolean | Or: no photos yet — we return an upload_url where your users add photos themselves (see below). |
webhook_url | URL | Where we POST the job when the preview is ready or the job fails. |
metadata | object | Your own key–value strings (order id, user id); returned unchanged in every response and webhook. |
One way of passing the tiles per job — pick whichever fits your system (next section). Send an Idempotency-Key header with every POST: a retried request with the same key returns the same job instead of creating a second one.
A mosaic needs one main photo and many tile photos. Send them the way your photos already live — we adapt to you, not the other way round.
tile_image_urls: public or pre-signed links. We download them in parallel and skip broken ones (the job reports how many were used).
tiles_zip_url: a single link to a ZIP with all the photos — handy when they sit on your server or in a file share.
tiles_source: Amazon S3, Google Cloud Storage or Azure Blob. Give us read-only access to a folder (we send you the policy) and we pull the photos directly — nothing to re-upload.
multipart/form-data with main_image and tiles — separate files or one ZIP. Best for smaller sets sent straight from your backend.
collect_uploads: true returns an upload_url: a mobile-friendly page (with your logo, if you like) where event guests, family or students add their own photos. Call POST /v1/mosaics/{id}/start when you have enough.
Dropbox or Google Drive folder, FTP, your photo platform's own API, a batch every night — tell us how your photos are stored and we'll set up a custom integration.
The main photo comes as main_image_url or as the main_image file. JPEG and PNG work everywhere; tell us if you need HEIC or RAW.
| Status | What happens |
|---|---|
collecting | Only with collect_uploads: the upload page is open; photos_received grows until you call /start. |
queued | Accepted, photos are being downloaded and checked. |
processing | The mosaic is being assembled on our render farm. This takes minutes and depends on the number of photos and the queue. |
preview_ready | preview_url is set: a watermarked image to show your user. |
unlocked | Credits spent, hd_url is set: the clean full-resolution file. |
failed | error says why (for example, too few readable photos). Failed jobs never spend credits. |
File links (preview_url, hd_url) are time-limited. Download the file or call GET /v1/mosaics/{id} again for a fresh link.
# GET /v1/mosaics/msc_7Hq2xV9 → 200 { "id": "msc_7Hq2xV9", "status": "preview_ready", "preview_url": "https://…/preview.jpg", "hd_url": null, "tiles_used": 300, "error": null, "metadata": {"order_id": "A-1042"}, "created_at": "2026-10-14T12:00:00Z" }
Errors come as JSON: {"error": "insufficient_credits", "message": "…"}.
| HTTP | error | When |
|---|---|---|
| 400 | invalid_request | A field is missing or wrong, a photo cannot be downloaded or read. |
| 401 | unauthorized | No key, wrong key or revoked key. |
| 402 | insufficient_credits | Not enough credits on the account to start or unlock. |
| 404 | not_found | No such job for this key. |
| 409 | preview_not_ready | Unlock called before preview_ready. |
| 429 | rate_limited | Too many requests or too many jobs running at once; see Retry-After. |
| 5xx | server_error | Our side. Retry with the same Idempotency-Key. |
Assembly runs on our own render farm, so each key has a limit on jobs running at the same time and on requests per minute. Limits are set per key and returned by GET /v1/account; for higher volume, write to us.
We POST a JSON event (mosaic.preview_ready, mosaic.unlocked, mosaic.failed) to your webhook_url. Each delivery has an X-Collager-Signature header: HMAC-SHA256 of the raw body with your webhook secret — check it before trusting the event. Answer 2xx within 10 seconds; otherwise we retry with growing pauses for up to 24 hours. Events can arrive twice — use the job id to ignore duplicates.
An API key belongs to an ordinary Collager.ai account. No separate developer plan and no separate billing.
Unlocking one HD mosaic spends one credit — the same credits you buy on collager.ai.
There is no self-serve free tier. We read every request and issue keys ourselves; for building an integration we can give you a test key that returns watermarked previews only.
High volume, white-label or your own watermark: write to us — volume terms are set by agreement.
Building an assistant, an agent or a no-code automation? These are the machine-readable entry points.
collager.ai/llms.txt — what Collager.ai is and where everything lives, in plain text for language models.
collager.ai/developers/openapi.yaml — the draft v1 specification.
MCP server: coming — request a key to be notified.
Tell us what you are building. We read every request and reply from support@collager.ai.