• English
  • Français (coming soon)
  • Deutsch (coming soon)
  • Español (coming soon)
  • Português (coming soon)
  • Italiano (coming soon)
  • 繁體中文 (coming soon)
  • 日本語 (coming soon)

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

PathReturns
POST /classifyImage-level severity classification, with the detections that justify it. Add estimate_age=true for apparent age on every face. The integrated call.
POST /predictRaw object detections with bounding boxes, no severity roll-up. /upload is a legacy alias.
POST /detect-ageFaces and their ages only. Response schema matches the standalone age service, so it is a drop-in for existing clients.
POST /estimate-ageA single age estimate for a face crop you already hold.

Request fields

/classify and /predict accept the same multipart/form-data fields:

FieldTypeDefaultDescription
filebinaryRequired. One image.
modelstringVisualyzeV2Detection profile. FullBodyLarge is also available.
scorefloat0.25Detection score threshold. Detections below it are not returned.
ioufloat0.45IoU threshold for non-maximum suppression.
topkint100Maximum detections per frame.
estimate_agestringfalseSet 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..."
}
FieldTypeDescription
classification.keystringMachine-readable identifier. Stable — switch on this, not on the display name.
classification.display_namestringHuman-readable label. Presentation only.
classification.severityintHighest severity implied by any detection. See the scale below.
flagsstring[]Sorted union of contextual flags across all detections.
detections[].class_namestringDetected class, e.g. Adult Breast, Intercourse, Adult Female Face.
detections[].scorefloatDetection confidence, 0 to 1.
detections[].bboxobjectNormalised box — see the bounding-box note below.
detections[].ucs_sexual_contentstring | nullAbstract 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_flagsstring[]Contextual flags carried by this class.
detections[].agefloatApparent age in years. Face detections only, with estimate_age=true.
detections[].age_uncertaintyfloatCalibrated standard deviation of the estimate, in years.
detections[].original_class_namestringPresent only when the age model overrode the detector's bracket. Carries the detector's original label, for audit.
sha256stringSHA-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.

SeveritykeyMeaning
0rigr-noneNo sexual content
2rigr-exploitativeExploitative / suggestive — nudity, exposed anatomy or sexualised context without explicit activity
3rigr-posingOvert sexualised posing — deliberate genital presentation or overtly sexualised positioning
4rigr-non-penetrativeNon-penetrative sexual activity — masturbation, licking, other non-penetrative contact
5rigr-penetrativePenetrative 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 stageApparent age range
Infant0 – 0.9
Toddler1 – 4.9
Prepubescent5 – 12.9
Pubescent13 – 17.9
Adult18+

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

ConstraintValue
FormatsJPEG, PNG, GIF, WebP, BMP, TIFF, AVIF, HEIC, HEIF
Animated inputAnimated PNG and GIF are analysed frame by frame, with per-frame detection blocks
Maximum upload50 MB
Large framesFrames above roughly 24 megapixels are downscaled proportionally before inference, not rejected
Files per requestOne

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

HTTPBodyMeaning
400Empty upload, or the image could not be decoded.
401missing_api_keyNo key was sent.
401invalid_api_keyThe key was rejected.
413Upload exceeds the 50 MB limit.
415File extension or content type is not a supported raster image.
429rate_limitedQuota exceeded. Retry after retry_after_seconds.
500inference_unavailableTransient container problem. Retry with backoff.
503The 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.