An age detection API estimates how old a person is from a photo of their face. You send an image, and you get back an age range for every face in it. This tutorial calls one from Python, then uses the result to label a group photo, bucket ages for analytics, and flag possible minors. We tested every script on public domain photos of people whose age is known.

Want to try it on your own photos? Get a key for the Face Analyzer API.

Results on photos with known ages

The two children were photographed by Lewis Hine, who wrote their age in his captions. The third photo is John Glenn's official NASA portrait from April 1998, taken when he was 76.

Photo Real age API age range Midpoint
Luigi, newsboy, Sacramento, 1915 6 6 to 12 9
Addie Card, mill spinner, Vermont, 1910 12 9 to 15 12
John Glenn, NASA portrait, 1998 76 73 to 79 76

All three real ages fall inside the returned range, and the midpoint is exact for two of them. Each request took about one second.

Detect age in Python

You need Python 3 with requests and a RapidAPI key in the RAPIDAPI_KEY environment variable. Save this as age_api.py:

import os import requests API_URL = "https://faceanalyzer-ai.p.rapidapi.com/faceanalysis" HEADERS = { "x-rapidapi-host": "faceanalyzer-ai.p.rapidapi.com", "x-rapidapi-key": os.environ["RAPIDAPI_KEY"], } def detect_ages(path): """Return one age estimate per face found in the image.""" with open(path, "rb") as f: r = requests.post(API_URL, headers=HEADERS, files={"image": f}, timeout=30) r.raise_for_status() data = r.json() if data.get("statusCode") != 200: raise RuntimeError(data["body"].get("error", "unknown error")) results = [] for face in data["body"]["faces"]: age = face["facialFeatures"]["AgeRange"] results.append({ "low": age["Low"], "high": age["High"], "estimate": (age["Low"] + age["High"]) / 2, "box": face["boundingBox"], }) # Sort faces left to right so the output order is stable return sorted(results, key=lambda f: f["box"]["topLeft"]["x"]) if __name__ == "__main__": for face in detect_ages("glenn.jpg"): print(f"Age {face['low']}-{face['high']} (estimate {face['estimate']:.0f})") 

On the John Glenn portrait it prints:

Age 73-79 (estimate 76) 

An image with no face returns an empty list rather than an error, so "no face" is a normal outcome.

What the response contains: facialFeatures.AgeRange holds Low and High in whole years. The midpoint is the best single number for charts and averages. boundingBox uses coordinates between 0 and 1, relative to the image size. The same object also returns gender, smile, glasses and emotions.

One detail: when a request fails (for example an image URL the API cannot download), the HTTP status is still 200 and the error is in statusCode and body.error. That is why the function checks statusCode and does not rely on raise_for_status alone.

Estimate the age of everyone in a group photo

Each face comes with its box, so you can draw the age range above every person (pip install pillow):

from PIL import Image, ImageDraw, ImageFont from age_api import detect_ages def annotate(path, out_path): img = Image.open(path).convert("RGB") w, h = img.size draw = ImageDraw.Draw(img) font = ImageFont.truetype("DejaVuSans-Bold.ttf", max(18, w // 40)) for face in detect_ages(path): tl, br = face["box"]["topLeft"], face["box"]["bottomRight"] box = (tl["x"] * w, tl["y"] * h, br["x"] * w, br["y"] * h) draw.rectangle(box, outline=(124, 58, 237), width=max(3, w // 300)) label = f"{face['low']}-{face['high']}" x, y = box[0], max(0, box[1] - font.size - 12) tw = draw.textlength(label, font=font) draw.rectangle((x, y, x + tw + 16, y + font.size + 10), fill=(124, 58, 237)) draw.text((x + 8, y + 4), label, fill="white", font=font) img.save(out_path, quality=90) annotate("artemis.jpg", "artemis_ages.jpg") 

On NASA's official Artemis II crew portrait, the API found all four faces in one request:

The four Artemis II astronauts, each face framed by a purple box with the estimated age range above it

Batch a folder and bucket ages for analytics

This script scans a folder, writes one CSV row per face and counts faces per age bracket:

import csv from collections import Counter from pathlib import Path from age_api import detect_ages BRACKETS = [(0, 12, "0-12"), (13, 17, "13-17"), (18, 24, "18-24"), (25, 34, "25-34"), (35, 44, "35-44"), (45, 54, "45-54"), (55, 64, "55-64"), (65, 200, "65+")] def bracket(age): for low, high, name in BRACKETS: if low <= age <= high: return name def scan_folder(folder, csv_path): counts = Counter() with open(csv_path, "w", newline="") as f: writer = csv.writer(f) writer.writerow(["file", "face", "low", "high", "estimate", "bracket"]) for path in sorted(Path(folder).glob("*.jpg")): for i, face in enumerate(detect_ages(path)): b = bracket(round(face["estimate"])) counts[b] += 1 writer.writerow([path.name, i, face["low"], face["high"], face["estimate"], b]) return counts counts = scan_folder("photos", "ages.csv") for name in [b[2] for b in BRACKETS]: print(f"{name:>6}: {counts[name]}") 

On our four test photos (seven faces), it prints:

 0-12: 2 13-17: 0 18-24: 0 25-34: 0 35-44: 4 45-54: 0 55-64: 0 65+: 1 

For analytics, the midpoint is the right input: errors in both directions cancel out over many faces. Running the same photos twice returned the same numbers.

Flag possible minors

For safety decisions, use the Low value and a buffer zone where you ask for more proof:

from age_api import detect_ages ADULT_AGE = 18 BUFFER = 7 # treat anyone who might be under 25 as "check further" def classify(path): faces = detect_ages(path) if not faces: return "no_face" youngest = min(f["low"] for f in faces) if youngest < ADULT_AGE: return "possible_minor" if youngest < ADULT_AGE + BUFFER: return "verify_id" return "adult" for name in ["luigi.jpg", "addie.jpg", "glenn.jpg", "artemis.jpg", "noface.jpg"]: print(name, classify(name)) 
luigi.jpg possible_minor addie.jpg possible_minor glenn.jpg adult artemis.jpg adult noface.jpg no_face 

The rule looks at the youngest face, so one child in a group picture is enough to route it for review. For a complete age gate with Flask, consent and data retention, see the face-based age verification tutorial. To choose a provider for regulated checks, see the comparison of age verification APIs.

Production tips

  • Upload the file when you can. The API also accepts a public image URL, but some hosts block server-side downloads. A Wikimedia Commons URL returned "Could not load image", while the same photo uploaded as a file worked.
  • Use ranges for decisions, midpoints for statistics. The API estimates apparent age. Studio lighting, makeup and retouching can make an adult look younger, which is another reason to decide on Low with a buffer.
  • Give the face enough pixels. In the Luigi photo the face covers less than 10% of the image width, and the API still found it.
  • Store the result, not the photo. Age is personal data in many jurisdictions: save the numbers, delete the image, and tell users what you process.

Full tutorial with images: Age Detection API in Python on ai-engine.net.

Sources

Try it on your own photos: grab a key from the Face Analyzer API page and run age_api.py.