Media Examiner API reference
Media Examiner detects nudity, exposed intimate anatomy and sexual activity while estimating apparent age and developmental stage — all from one image-analysis API. One image, one call: no chaining a generic adult-content classifier to a separate age service.
The machine-readable contract is published as an OpenAPI 3.1 document, so you can generate a client rather than hand-writing one. For what the models do and where they are used, read the capability overview. If you only need ages, the standalone age estimation API remains available.
Apparent age is an estimate, not a verification. Apparent-age and developmental-stage outputs are visual estimates. They do not verify identity or establish a person's legal age. High-risk and borderline decisions should include appropriate human review.
Quick start
The hosted service is at https://api.mes.rigr.ai. Send the image as
multipart form data and opt into age estimation with estimate_age=true.
curl -X POST https://api.mes.rigr.ai/classify \
-H "X-API-KEY: YOUR_KEY" \
-F "[email protected]" \
-F "model=VisualyzeV2" \
-F "estimate_age=true" Authentication
Every request needs an API key. Two header forms are accepted and are equivalent — use whichever suits your client:
X-API-KEY: your_api_key_here
Authorization: Bearer your_api_key_here Keys are issued by Rigr AI — talk to us about an evaluation key.
Endpoints
| Path | Returns |
|---|---|
POST /classify | Image-level severity classification, with the detections that justify it. Add estimate_age=true for apparent age on every face. The integrated call. |
POST /predict | Raw object detections with bounding boxes, no severity roll-up. /upload is a legacy alias. |
POST /detect-age | Faces and their ages only. Response schema matches the standalone age service, so it is a drop-in for existing clients. |
POST /estimate-age | A single age estimate for a face crop you already hold. |
Request fields
/classify and /predict accept the same multipart/form-data fields:
| Field | Type | Default | Description |
|---|---|---|---|
file | binary | — | Required. One image. |
model | string | VisualyzeV2 | Detection profile. FullBodyLarge is also available. |
score | float | 0.25 | Detection score threshold. Detections below it are not returned. |
iou | float | 0.45 | IoU threshold for non-maximum suppression. |
topk | int | 100 | Maximum detections per frame. |
estimate_age | string | false | Set to true to run facial age estimation on detected faces in the same inference pass. Age is opt-in — it does not run unless you ask for it. |
POST /classify
A complete integrated response — image-level classification, an anatomy detection, a sexual-activity detection, and a face carrying apparent age with its uncertainty:
{
"classification": {
"key": "rigr-penetrative",
"display_name": "Penetrative sexual activity",
"severity": 5
},
"flags": ["Self-Generated"],
"detections": [
{
"class_name": "Male Receive Oral",
"score": 0.87,
"bbox": { "x": 0.12, "y": 0.45, "w": 0.31, "h": 0.62 },
"ucs_sexual_content": "Penetrative Sexual Activity",
"ucs_flags": ["Self-Generated"]
},
{
"class_name": "Pubescent Breast",
"score": 0.71,
"bbox": { "x": 0.44, "y": 0.30, "w": 0.12, "h": 0.11 },
"ucs_sexual_content": "Exploitative",
"ucs_flags": []
},
{
"class_name": "Pubescent Female Face",
"score": 0.92,
"bbox": { "x": 0.50, "y": 0.17, "w": 0.09, "h": 0.18 },
"ucs_sexual_content": null,
"ucs_flags": [],
"age": 14.8,
"age_uncertainty": 2.1,
"original_class_name": "Prepubescent Female Face"
}
],
"filename": "image.jpg",
"model": "VisualyzeV2",
"sha256": "d2ff9e6b..."
} | Field | Type | Description |
|---|---|---|
classification.key | string | Machine-readable identifier. Stable — switch on this, not on the display name. |
classification.display_name | string | Human-readable label. Presentation only. |
classification.severity | int | Highest severity implied by any detection. See the scale below. |
flags | string[] | Sorted union of contextual flags across all detections. |
detections[].class_name | string | Detected class, e.g. Adult Breast, Intercourse, Adult Female Face. |
detections[].score | float | Detection confidence, 0 to 1. |
detections[].bbox | object | Normalised box — see the bounding-box note below. |
detections[].ucs_sexual_content | string | null | Abstract category this class maps to. null for classes carrying no sexual-content meaning of their own (faces, clothing, jewellery, life-stage classes); these contribute no severity. |
detections[].ucs_flags | string[] | Contextual flags carried by this class. |
detections[].age | float | Apparent age in years. Face detections only, with estimate_age=true. |
detections[].age_uncertainty | float | Calibrated standard deviation of the estimate, in years. |
detections[].original_class_name | string | Present only when the age model overrode the detector's bracket. Carries the detector's original label, for audit. |
sha256 | string | SHA-256 of the uploaded bytes, for your own audit trail. |
Bounding boxes on /classify and /predict are normalised
x/y/w/h with a top-left origin —
an object, not a corner pair, and fractions of the frame rather than pixels. Multiply by
the frame's width and height to draw them. /detect-age is the exception: it
returns pixel [x_min, y_min, x_max, y_max], because it mirrors the standalone
age service's schema.
Severity scale
Severity is the highest level implied by any single detection in the image.
| Severity | key | Meaning |
|---|---|---|
| 0 | rigr-none | No sexual content |
| 2 | rigr-exploitative | Exploitative / suggestive — nudity, exposed anatomy or sexualised context without explicit activity |
| 3 | rigr-posing | Overt sexualised posing — deliberate genital presentation or overtly sexualised positioning |
| 4 | rigr-non-penetrative | Non-penetrative sexual activity — masturbation, licking, other non-penetrative contact |
| 5 | rigr-penetrative | Penetrative sexual activity — intercourse, oral sex, anal or vaginal penetration |
The scale is 0, 2, 3, 4, 5 — the classifier never emits 1. Level 1 exists in the wider review scale as an analyst verdict (explicit adult material judged benign on review), which no model assigns. Size your switch statements accordingly. This is a triage scale, not a statutory one: categories and thresholds differ by jurisdiction, and you should map it to the rules your team works under rather than treating any single level as a universal decision boundary.
Contextual flags
Flags are orthogonal to severity — they describe the character of the material rather
than how explicit it is. Four values are emitted:
Self-Generated, Sadomasochism, CG Elements and
Bodily Fluids.
Apparent age and developmental stage
With estimate_age=true, a dedicated facial age estimation model runs on
every detected face in the same pass. Each face detection gains age and
age_uncertainty. The age model is more accurate than the detector's own
bracket labels, so where the two disagree the class_name is overridden and
the detector's original label is preserved as original_class_name.
| Developmental stage | Apparent age range |
|---|---|
| Infant | 0 – 0.9 |
| Toddler | 1 – 4.9 |
| Prepubescent | 5 – 12.9 |
| Pubescent | 13 – 17.9 |
| Adult | 18+ |
age_uncertainty is the point of the calibration: it is what lets you route
rather than guess. A tight interval well clear of your threshold can be decided
automatically; a wide one straddling it should go to a human. Treating the point
estimate as a verdict and discarding the uncertainty throws away the signal you most
need on the borderline cases.
POST /detect-age
Detects faces and estimates their ages, returning nothing else. The schema matches the standalone Rigr age estimation service, so a client already built against that service moves across by changing the URL.
{
"error": null,
"results": [
{
"idx": 0,
"error": null,
"results": [
{ "idx": 0, "age": 14.8, "uncertainty": 2.1,
"bbox": [320, 85, 378, 171], "score": 0.9234,
"source": "Pubescent Female Face" }
]
}
]
} results holds one entry per frame; a still image has a single entry at
index 0. Face detection on this path defaults to a score of 0.6 rather
than 0.25 — quality is expressed through the per-face uncertainty rather than by
dropping faces at detection time.
POST /estimate-age
Send a cropped face as file; no object detection runs.
{ "age": 14.8, "uncertainty": 2.1, "age_bracket": "Pubescent" } Image requirements
| Constraint | Value |
|---|---|
| Formats | JPEG, PNG, GIF, WebP, BMP, TIFF, AVIF, HEIC, HEIF |
| Animated input | Animated PNG and GIF are analysed frame by frame, with per-frame detection blocks |
| Maximum upload | 50 MB |
| Large frames | Frames above roughly 24 megapixels are downscaled proportionally before inference, not rejected |
| Files per request | One |
Animated inputs scale roughly linearly with frame count, so a long GIF can take tens of seconds. Size client timeouts on frame count rather than on file size, and add transport latency plus a safety buffer.
Rate limits
The default quota is 120 requests per 60 seconds per API key. Every
response carries x-rate-limit-remaining, and a 429 includes
retry_after_seconds. Back off exponentially — 1s, 2s, 4s, up to 30s.
Contact us if you need more headroom; the quota is per-plan, not a hard service limit.
Error handling
| HTTP | Body | Meaning |
|---|---|---|
| 400 | — | Empty upload, or the image could not be decoded. |
| 401 | missing_api_key | No key was sent. |
| 401 | invalid_api_key | The key was rejected. |
| 413 | — | Upload exceeds the 50 MB limit. |
| 415 | — | File extension or content type is not a supported raster image. |
| 429 | rate_limited | Quota exceeded. Retry after retry_after_seconds. |
| 500 | inference_unavailable | Transient container problem. Retry with backoff. |
| 503 | — | The age model is not loaded on this deployment (age endpoints only). |
Deployment
Hosted. The fastest way to evaluate is
https://api.mes.rigr.ai — no infrastructure setup required.
Containerised and air-gapped. Media Examiner ships as a container exposing the same REST API, plus a gRPC interface for streaming uploads, so only the host changes. This is how it is deployed where material cannot leave the building. Contact us for registry access and deployment guidance.
Getting access
Talk to us about an evaluation key Download the OpenAPI document
Also available: the CSAM Classification capability overview, the Trust & Safety solution page for moderation and review-routing workflows, and the standalone age estimation API reference.