Guides
How to build a face recognition photo-sharing platform
The full stack behind 'upload a selfie, get your photos' — ingestion to delivery, built privacy-first.
TL;DR
A face-search photo-sharing platform has five stages: ingest photos (ideally camera-to-cloud during the event), index each event's faces into an isolated collection, let guests retrieve by selfie, assemble a personal gallery from the matches, and deliver it (link, WhatsApp, or email) — then delete on a retention schedule. The face API handles detection and matching; you own storage, the event/guest data model, delivery, and consent. This guide lays out the whole architecture with the data model and the hot-path code.
Platforms like this power weddings, conferences, marathons, and festivals — the pitch is "guests find themselves in seconds instead of scrolling thousands of photos." The face recognition is one call; the platform is everything around it. Here's the whole thing.
The five stages
- Ingest — photographers push photos to your storage during the event (camera-to-cloud) or upload in batches after.
- Index — each new photo is indexed into that event's collection so its faces are searchable.
- Retrieve — a guest scans a QR code, uploads a selfie, and you search the event collection.
- Assemble — map the matched photo ids back to your stored originals and build that guest's personal gallery.
- Deliver — send the gallery via link, WhatsApp, or email; offer HD downloads, prints, albums.
Data model
create table event (
id text primary key, -- also the collection id
name text not null,
retention_at timestamptz not null, -- delete faces + photos after this
created_at timestamptz not null default now()
);
create table photo (
id text primary key, -- your id; passed as photoId at index
event_id text not null references event(id),
storage_url text not null,
indexed boolean not null default false,
face_count int
);
create table guest (
id text primary key,
event_id text not null references event(id),
contact text, -- phone/email for delivery
consented_at timestamptz not null -- opted in by uploading a selfie
);Stage 1-2 — ingest and index
The win everyone wants is speed: photos should be searchable within minutes of the shutter. Index as photos land rather than in one big post-event job. Use the batch endpoint for throughput and let per-photo results arrive on a webhook so indexing never blocks the guest-facing path.
import os, requests
BASE = "https://api.sightradar.com"
H = {"Authorization": f"Bearer {os.environ['SR_API_KEY']}"}
def ensure_event(event_id):
requests.post(f"{BASE}/v1/collections", headers=H,
json={"collection_id": event_id})
def index_new_photos(event_id, photos): # [(photo_id, url), ...]
for i in range(0, len(photos), 1000): # up to 1000 per batch
chunk = photos[i:i+1000]
requests.post(f"{BASE}/v1/batches",
headers={**H, "Idempotency-Key": f"{event_id}-{i}"},
json={"collection_id": event_id, "op": "index",
"photos": [{"external_id": p, "url": u} for p, u in chunk]}
).raise_for_status()Tip: Guests upload selfies from phones, so they're HEIC; event photos from a CDN are often WebP. An API that decodes both natively (rather than JPEG/PNG only) removes a transcode stage from your hot path — see the image-formats guide.
Stage 3-4 — selfie retrieval and personal gallery
def guest_gallery(event_id, guest_id, selfie_url):
record_consent(guest_id, event_id) # store opt-in before the biometric op
r = requests.post(f"{BASE}/v1/collections/{event_id}/search",
headers=H, json={"url": selfie_url, "limit": 200})
r.raise_for_status()
data = r.json()
if data.get("reason"): # no_face / low_quality — ask to retry
return {"error": "Please upload a clearer selfie."}
# 'auto' matches show now; 'review' matches behind a 'confirm it's you' step
photo_ids = [(m["photo_id"], m.get("tier", "auto")) for m in data["matches"]]
return build_gallery(guest_id, photo_ids) # map ids -> your storage_urlsReveal high-confidence (auto) matches immediately; put borderline (review) ones behind a lightweight "is this you?" confirm so a stranger's photos are never auto-shown to the wrong guest. The score is on a 0-1 scale — calibrate your reveal cutoff on real data (how to choose it).
Stage 5 — delivery
A personal gallery is a delivery surface: send a private link, push over WhatsApp or email, and offer HD downloads and print upsells. Because each gallery is scoped to one guest, it's also your monetisation and re-engagement channel — the thing a public folder can never be.
Privacy & retention (build it in, don't bolt it on)
- Per-event isolation — search only inside the event's collection; never across events.
- No public index — a photo is returned only to a matching selfie, never at a guessable URL.
- Two consents — disclose face-tagging to attendees (indexing basis) and capture the guest's opt-in at selfie upload (search).
- Retention job — at
event.retention_at, delete the collection (faces) and the stored photos; keep only an audit record of the deletion. - Removal on request — a person can ask to be removed before retention expires.
def run_retention(event_id):
r = requests.delete(f"{BASE}/v1/collections/{event_id}", headers=H)
r.raise_for_status() # abort + retry if API delete fails
delete_stored_photos(event_id)
audit(event_id, "event_data_deleted")What to reuse vs build
| Concern | Use the API | You build |
|---|---|---|
| Face detection + matching | ✅ index / search | — |
| Photo storage & CDN | — | ✅ S3/R2 + your URLs |
| Event/guest data model | — | ✅ your DB |
| Personal gallery + delivery | — | ✅ UI + WhatsApp/email |
| Isolation & scoring primitives | ✅ collections + tiers | ✅ your thresholds |
Index an event and run your first selfie retrieval on trial credits.
Start the quickstartFrequently asked questions
How do I build a photo-sharing platform with face recognition?
Five stages: ingest photos (camera-to-cloud during the event is ideal), index each event's faces into its own isolated collection, let guests retrieve by uploading a selfie, assemble a personal gallery by mapping matched photo ids back to your stored originals, and deliver it by link, WhatsApp, or email. The face API handles detection and matching; you own storage, the event/guest data model, delivery, consent, and a retention schedule that deletes data afterward.
How fast can guests get their photos?
If you index photos as they're ingested (rather than in one post-event batch) and retrieve by selfie, guests can get their personal gallery within minutes of a photo being taken. Use the batch index endpoint for throughput with per-photo results delivered to a webhook so indexing never blocks the guest-facing search path.
How do I keep a photo-sharing platform private?
Build privacy into the architecture: search only within a single event's collection (per-event isolation), never expose photos at a guessable public URL (a photo is returned only to a matching selfie), capture consent both for indexing attendees and for each guest's selfie search, and run a retention job that deletes faces and photos on a schedule or on request while keeping an audit record of the deletion.
What does it cost to run per event?
Cost is per photo processed. Indexing is a one-time cost per photo at the batch rate, and each guest's selfie search is a per-search cost — both fractions of a cent, so even a large free event is inexpensive. See the cost breakdown article for worked per-event scenarios and the calculator on the pricing page.