After a wedding, a conference or a race, the photographer delivers 2,000 photos and every guest asks: which ones am I in? A face search fixes it. The guest takes one selfie, and the gallery shows only the photos that contain them.

Below is the full flow in Python, with real results from a live 18-photo birthday gallery.

Want to try it on your own gallery? Get a key for the Face Analyzer API and point the script at a folder of event photos.

How it works

  1. Index the gallery once. Each event photo goes to a face repository. Every face in it is stored, labeled with the photo's file name.
  2. Search with a selfie. Each match carries the label of the photo it came from, a similarity score and the face's position.

A group photo with five people produces five stored faces with the same label. The label turns a face match into a photo match.

Step 1: one repository per event

import os import requests from concurrent.futures import ThreadPoolExecutor BASE = "https://faceanalyzer-ai.p.rapidapi.com" HEADERS = { "x-rapidapi-host": "faceanalyzer-ai.p.rapidapi.com", "x-rapidapi-key": "YOUR_API_KEY", } EVENT = "birthday-2026-10-17" requests.post( f"{BASE}/create-facial-repository", headers=HEADERS, data={"repository_id": EVENT}, ) 

Step 2: index every event photo

def index_photo(path): """Save every face in one event photo, labeled with the photo's file name.""" with open(path, "rb") as f: r = requests.post( f"{BASE}/save-face-in-repository", headers=HEADERS, files={"image": f}, data={ "repository_id": EVENT, "external_id": os.path.basename(path), "max_faces": "20", }, ) return os.path.basename(path), len(r.json()["body"].get("FacesId", [])) photos = sorted(os.path.join("event_photos", n) for n in os.listdir("event_photos")) with ThreadPoolExecutor(max_workers=4) as pool: for name, faces in pool.map(index_photo, photos): print(name, faces, "faces indexed") 

18 photos, 76 faces, one call per photo.

Step 3: find a guest's photos

MIN_SIMILARITY = 95 def find_my_photos(selfie_path): """Return the event photos that contain the person in the selfie.""" with open(selfie_path, "rb") as f: r = requests.post( f"{BASE}/search-face-in-repository", headers=HEADERS, files={"image": f}, data={"repository_id": EVENT}, ) body = r.json()["body"] if "error" in body: return None # no face found in the selfie: ask for a new one  best = {} for m in body["FaceMatches"]: photo = m["ExternalImageId"] if m["Similarity"] >= MIN_SIMILARITY and ( photo not in best or m["Similarity"] > best[photo]["Similarity"] ): best[photo] = m return sorted(best.values(), key=lambda m: m["Similarity"], reverse=True) matches = find_my_photos("selfie.jpg") print(len(matches), "photos found") 
14 photos found IMG_7336946.jpg 100.0 IMG_7337110.jpg 99.99 ... IMG_7336992.jpg 98.89 

She appears in exactly those 14 photos, and the 4 left out are the ones she is not in. Five-person group shots, a side view and a face cut by the frame edge all matched. The search took about half a second.

Face search results on an 18-photo birthday gallery: 14 photos with the guest's face boxed at 98.89 to 100% similarity, 4 photos she is not in

Edge cases: a selfie with no face returns an error in the body (handled above), and a person who was not at the event gets an empty FaceMatches list.

Step 4: highlight the guest

from PIL import Image, ImageDraw def highlight(photo_path, match, out_path): """Draw a box around the guest's face in one matched photo.""" im = Image.open(photo_path).convert("RGB") w, h = im.size box = match["BoundingBox"] rect = [ box["topLeft"]["x"] * w, box["topLeft"]["y"] * h, box["bottomRight"]["x"] * w, box["bottomRight"]["y"] * h, ] ImageDraw.Draw(im).rectangle(rect, outline=(52, 211, 153), width=6) im.save(out_path) 

The whole script (create, index 18 photos, search, highlight 14) ran in under 8 seconds.

Production tips

  • Ask for a clear, front-facing selfie
  • Show guests only their own matches
  • Get consent before the selfie, do not store it, and call /delete-facial-repository when the gallery closes
  • Index new uploads as the photographer delivers them
  • Genuine matches scored 98.89 and above, so a 95 cutoff keeps a wide margin

More on the full guide at ai-engine.net, including where selfie search fits (photographers, races, weddings, conferences, schools).

Sources: GDPR Article 9 (biometric data), test photos by Polina Tankilevitch on Pexels.

👉 Grab a key from the Face Analyzer API page and run a search with your own selfie.