This article was written with AI.
Disclosure: I work on SightRadar, the API this team moved to. The figures for sellphotos.online are the ones they reported, and the full case study is at sightradar.com/customers/sellphotos. The audit scripts below run against your own Rekognition account, whatever you decide afterwards.
sellphotos.online sells event photos. Photographers upload weddings, marathons and corporate events, a guest uploads a selfie, and face search finds every photo that guest is in. They ran it on AWS Rekognition until the bill reached about $3,500 a month. After moving to SightRadar they pay about $1,550, for the same photos and the same number of searches. By their figures that is 56% less. They also found more faces in crowd shots, which had been their bigger complaint.
This post breaks a Rekognition face search bill into its three lines, shows how to measure each one from your own account, and then covers what changed for them.
The three lines on a face search bill
- Indexing. Every
IndexFacescall is billed per image. In us-east-1 the first million Group 1 images a month cost $0.0010 each. - Searching.
SearchFacesByImage(search with a selfie) andSearchFaces(search with a stored face id) are Group 1 calls at the same per-image rate. - Storage. Every face you keep in a collection is a stored face vector, billed at $0.00001 per vector per month, which is $10 per million faces. AWS applies the charge monthly and pro-rates partial months (pricing page). User vectors are billed the same way.
The first two lines follow traffic. The third follows what you keep, so it is charged every month whether anyone searches the collection or not. It is also per face, not per photo: a group shot with 40 people in it adds up to 40 vectors.
For a young platform, storage is small. For an event platform that keeps years of past events searchable, it becomes a fixed monthly cost that keeps growing unless you delete old collections.
The chart is list-price arithmetic, not sellphotos.online's data. Their case study does not attribute any of their saving to storage.
Measure your storage line
DescribeCollection returns FaceCount and UserCount for each collection. Multiply by the list price and you have the storage line before the invoice arrives. Collections are regional, so run it for every region you use, and change the price if yours differs from us-east-1:
import boto3 REGION = "us-east-1" PER_VECTOR_MONTH = 0.00001 # us-east-1 list price for face and user vectors rek = boto3.client("rekognition", region_name=REGION) total = 0 for page in rek.get_paginator("list_collections").paginate(): for cid in page["CollectionIds"]: c = rek.describe_collection(CollectionId=cid) vectors = c["FaceCount"] + c.get("UserCount", 0) total += vectors print(f"{cid:40} {vectors:>13,} ${vectors * PER_VECTOR_MONTH:>9,.2f}/month") print(f"{'total':40} {total:>13,} ${total * PER_VECTOR_MONTH:>9,.2f}/month") Sort the output and look at the biggest collections. If some belong to events nobody has searched in a year, the cheapest fix needs no migration at all: set a retention window and delete collections once they pass it.
Count the faces you are not indexing
Cost was half of their problem. The other half was guests who searched and came up empty because their face in the back row of a group shot was never indexed.
Rekognition tells you about some of those faces. IndexFaces returns UnindexedFaces, each with a list of Reasons such as SMALL_BOUNDING_BOX, LOW_FACE_QUALITY or EXCEEDS_MAX_FACES. By default, IndexFaces chooses the quality bar that filters detected faces. It also indexes at most 100 faces per image. When it has more faces than MaxFaces, it drops the lowest quality ones first, then the smallest (API reference).
Run a sample of your hardest photos into a scratch collection and tally the reasons:
import re from collections import Counter import boto3 REGION = "us-east-1" rek = boto3.client("rekognition", region_name=REGION) BUCKET = "your-photo-bucket" # must be in the same region as the client sample_keys = [...] # a few hundred crowd shots from one event SCRATCH = "audit-scratch" rek.create_collection(CollectionId=SCRATCH) indexed, reasons = 0, Counter() try: for key in sample_keys: res = rek.index_faces( CollectionId=SCRATCH, Image={"S3Object": {"Bucket": BUCKET, "Name": key}}, # ExternalImageId allows only [a-zA-Z0-9_.\-:], up to 255 characters ExternalImageId=re.sub(r"[^A-Za-z0-9_.\-:]", "_", key)[:255], ) indexed += len(res["FaceRecords"]) print(f"{key}: {len(res['FaceRecords'])} indexed, {len(res['UnindexedFaces'])} dropped") for face in res["UnindexedFaces"]: reasons.update(face["Reasons"]) finally: rek.delete_collection(CollectionId=SCRATCH) # stored faces are billed until deleted print("indexed:", indexed) for reason, n in reasons.most_common(): print(f"dropped ({reason}): {n}") 500 photos cost $0.50 to index at the first-tier rate. Two limits on what this tells you:
-
UnindexedFacesonly lists faces the detector found and then dropped. A face it never detected does not appear anywhere in the response. - AWS documents a minimum detectable face of 40 by 40 pixels in a 1920 by 1080 image (limits). In a wide group shot, the back rows are often near that size.
So finish the audit by eye. Take 20 group photos, count the faces yourself, and compare against FaceRecords. The gap is faces in those photos that no search can match as they are indexed today.
What sellphotos.online found
They did not switch on the strength of a pricing page. They indexed about 5,000 photos from a recent wedding into a test collection and compared search results with Rekognition's on the same photos. Then, for a day, every live guest search went to both providers.
SightRadar found more faces in group shots. Its detector scans each photo at several scales, which catches smaller faces that a single-scale pass misses. Ranking order differed slightly in a few searches, but every face Rekognition found, SightRadar found too. Within the first month the support team noticed fewer "my photos didn't show up" complaints. They did not measure it formally.
One trade-off to plan for: finding more faces means storing more faces. Every extra face in the back row is another vector on the storage line. At these prices that is small, but put it in the estimate.
The move also had costs of its own. Re-indexing the back catalogue took longer than they hoped, and they paid both providers while it ran. They spent a day re-deriving their match threshold, because Rekognition scores similarity from 0 to 100 and SightRadar from 0 to 1, and a cut-off copied from one model is wrong on another. The threshold guide covers the method.
Their bill
The bill went from about $3,500 to about $1,550 a month. Those are the totals they reported, without a line-by-line breakdown. At list prices no single line is more than 40% cheaper, so the rate gap alone does not account for 56%. Treat it as their result, not a rate to plan on, and run your own numbers.
List prices, line by line
| Line | AWS Rekognition (us-east-1, first tier) | SightRadar |
|---|---|---|
| Index a photo | $0.0010 | $0.00062 batch, $0.00093 real-time |
| Search with a selfie | $0.0010 | $0.00093 (billed as a real-time photo) |
| Search with a stored face id | $0.0010 | $0.00062 |
| Keep 1M faces for a month | $10.00 | $6.00 |
Batch indexing is 38% under Rekognition's first tier, real-time calls are 7% under, and storage is 40% under. SightRadar charges storage daily for the faces held that day. Creating and deleting collections is free.
A worked example from the pricing page: 100,000 photos indexed in a month, 2,400 selfie searches and 300,000 stored faces.
- Rekognition: $100.00 indexing + $2.40 search + $3.00 storage = $105.40
- SightRadar: $62.00 batch indexing + $2.23 search + $1.80 storage = $66.03
At that size storage is a rounding error. At 50 million faces it is $500 a month on Rekognition and $300 on SightRadar, charged in a month when nobody searches at all.
Where the gap narrows
- High volume. Rekognition's per-image rate falls in tiers: $0.0008 after the first million Group 1 images a month, $0.0006 after 5 million, $0.0004 after 35 million. Every image past the first million costs less on Rekognition than SightRadar's real-time rate. Averaged over the month, an all-real-time workload costs less on Rekognition from about 1.5 million images, and an all-batch workload from about 37 million.
- You use more than faces. Labels, text detection, celebrity recognition, video and user vectors are Rekognition features SightRadar does not support. The shim raises
NotImplementedErrorfor them. - You are not on Python. The Rekognition shim, which let them keep their boto3 integration, is Python only. On Node or Java you map the calls to the REST API yourself.
Your saving depends on how much of your volume can go through batch, how many billed calls you make per photo, how many faces you keep, and how carefully you calibrate the threshold. Run the two scripts above against your own account before you decide anything.
Further reading
- The full case study: How sellphotos.online cut their face recognition bill
- SightRadar pricing and cost calculator
- Migrating from AWS Rekognition
- SightRadar vs AWS Rekognition
- sellphotos.online
Disclosure again: I work on SightRadar. Prices are us-east-1 list prices at the time of writing, November 2026. Results depend on your photo mix, your volume, how many faces you keep, and how carefully you calibrate thresholds.





