All articles

Guides

Detecting duplicate accounts by face (without wrongful bans)

Catch the fraud ring, not your legitimate user with a twin. A careful design for face-based dedup.

By SightRadar EngineeringUpdated 10 min read Markdown

TL;DR

To catch duplicate accounts by face: enrol each new user's face into a single collection at signup (with consent), search the collection for near-matches, and route hits to a review queue rather than an automatic ban. A single similarity score is not proof of fraud — twins, family members, and poor-quality photos all produce high scores. Calibrate the threshold on your own data, keep a human in the loop for consequential decisions, and give users an appeals path. This guide builds that pipeline.

Note: Never auto-ban an account on one face-match score. Identical twins, close relatives, and re-used stock photos all create legitimate high-similarity collisions. A wrong ban on biometric evidence is both a terrible user experience and a legal risk. Face match is a signal that feeds review, not a verdict.

The pipeline

  1. Enrol at signup — with the user's consent, index their face into a single dedup collection and store the returned face id against their account.
  2. Search before you trust — search the collection with the new face; any near-match is a *candidate* duplicate, not a confirmed one.
  3. Score and bucket — high-confidence matches go to a fast-track review; borderline ones to a lower-priority queue; no match clears immediately.
  4. Human review for consequences — a reviewer looks at the flagged pair before any account action, with context (signup metadata, device, payment).
  5. Appeal path — a user told they're a duplicate can contest it, and a human can clear a false match.
DEDUP = "fraud__dedup"   # one collection, server-owned name

def on_signup(user_id, selfie_url):
    record_consent(user_id, purpose="duplicate_detection")
    # Search FIRST — does this face already exist?
    r = requests.post(f"{BASE}/v1/collections/{DEDUP}/search",
                      headers=H, json={"url": selfie_url, "limit": 5})
    r.raise_for_status()
    matches = [m for m in r.json().get("matches", []) if m["score"] >= REVIEW_MIN]
    # Then enrol this user's face so future signups are checked against it.
    requests.post(f"{BASE}/v1/collections/{DEDUP}/index", headers=H,
                  json={"url": selfie_url, "photoId": user_id}).raise_for_status()
    if matches:
        queue_for_review(user_id, matches)   # never auto-act here
    return matches

REVIEW_MIN = 0.80   # native 0-1 score; calibrate on YOUR data
Index the new face, then search the dedup collection for prior matches.

Tip: SightRadar's native score is on a 0-1 scale (AWS Rekognition's is 0-100). Pick REVIEW_MIN and any auto-confirm bar from a labelled validation set of real duplicate and non-duplicate pairs — see the threshold guide. Don't copy a number.

Why review beats auto-action

High score can mean…Right response
A real duplicate / fraud ringConfirm in review, then act per policy
Identical twin or siblingHuman clears it; not a duplicate
Re-used stock or celebrity photoFlag the photo, not the person
Same person, legitimate second accountDepends on your ToS — review decides

Enrolling a face for dedup is biometric processing — you need a lawful basis and should disclose it at signup. Keep the dedup collection scoped to that purpose, don't reuse it for anything else, and delete a user's face when they close their account or you no longer need it. Store the review decisions and audit trail; you don't need to keep the raw selfie once the face is indexed.

See the search and index operations you'd build this on.

Read the API reference

Frequently asked questions

Can I use face recognition to detect duplicate or fake accounts?

Yes — enrol each new user's face into a single dedup collection at signup (with consent) and search that collection before trusting a new account. Any near-match is a candidate duplicate. Route candidates to human review rather than auto-banning: identical twins, relatives, and reused photos all produce legitimate high scores, so a single similarity score is a signal, not proof.

Should I automatically ban an account that matches an existing face?

No. A wrongful ban based on one biometric score is both a bad user experience and a legal risk, because twins, family members, and duplicate stock photos create legitimate high-similarity collisions. Use the match to open a review with context, keep a human in the loop for any consequential action, and give users an appeals path to contest a false match.

What threshold should I use for duplicate detection?

There's no universal number, and SightRadar's native score is on a 0-1 scale (not Rekognition's 0-100). Build a validation set of known duplicate and non-duplicate pairs from your own data, measure false-match and false-non-match rates across thresholds, and pick an operating point that routes borderline cases to review rather than auto-acting.

Keep reading