Dev.to WebDev 🛠 Dev 👁 0 📖 3 min read

Find Event Photos by Selfie with a Face Search API (Python)

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 con

Find Event Photos by Selfie with a Face Search API (Python)

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.

📰 Read the original article on Dev.to WebDev

Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.