The safety check
POSThttps://api.kennmarq.com/v1/check
From 2 December 2026 the AI Act bans AI systems that make sexual pictures of anyone appearing to be a child, or of a real person without their consent (Art. 5(1)(ba) and (bb)). The safety check screens a picture for both, as a block, flag or allow. It's a risk signal with published error rates, not a guarantee: you decide, and you stay responsible.
Request
A key with the check scope, and multipart/form-data with the picture in file (PNG, JPEG or WebP; the request is at most 4 MB, and a larger picture, up to 25 MB, goes through an upload named in upload) and, optionally, policy: ai-act (the default) or safe-only. Each check counts two marks. To check while marking, send check to POST /v1/mark instead.
A clip (MP4, up to 25 MB here; check a larger one while marking it) is read on 12 frames spread over it, two marks each: the answer is its worst frame's (a block over a flag over an allow), with that frame's place in frame (index and seconds) and frames, how many were read.
curl https://api.kennmarq.com/v1/check \
-H "Authorization: Bearer $KENNMARQ_API_KEY" \
-F file=@render.png -F policy=ai-act{
"object": "safety_check",
"checked_at": "2026-11-12T10:00:00Z",
"checker": { "name": "Kennmarq.com", "version": "0.5.0" },
"file": { "type": "image/png", "bytes": 1482113, "width": 1024, "height": 1024, "sha256": "…" },
"policy": "ai-act",
"verdict": "flag",
"reason": "sexual_realistic_face",
"nudity": { "label": "explicit", "score": 0.991 },
"apparent_minor": false,
"realistic_face": true
}verdict:allow,flagorblock. A block is still a200: the check worked, and its answer is the block.reason:sexual_apparent_minor,sexual_realistic_face,explicit, ornullfor an allow.nudity:none,suggestiveorexplicit, and the classifier's score, 0 to 1.apparent_minorandrealistic_face: read only for a picture with nudity, andnullotherwise. The check never estimates ages on their own, and never returns an age or an identity.
Policies
| Policy | Blocks | Flags |
|---|---|---|
ai-act | Sexual or suggestive content with an apparent minor (Art. 5(1)(bb)) | Explicit content with a realistic face: get the person's consent before you publish it (Art. 5(1)(ba)) |
safe-only | The same, and anything explicit | Nothing |
| our floor | Explicit content with an apparent minor, on every POST /v1/mark whatever its check. You can't turn it off | Nothing |
How a picture is read
In steps, each only when the one before calls for it, so a picture without nudity costs one read. Nudity first: explicit when the classifier is sure, or fairly sure and the detector finds an exposed part; suggestive from a score of 0.3. Then, for a picture with nudity: is it a photo? For a photo, each face large enough to read (32 px or more) is read for apparent age, at stricter lines on an explicit picture than on a suggestive one. A drawing goes to the tagger instead, whose tags say when a drawn character is a child.
| Read | Model | Licence |
|---|---|---|
| Nudity | Falconsai's NSFW classifier (ViT-B/16), with NudeNet's 320n detector as a second opinion on borderline pictures | Apache-2.0; MIT |
| Photo or drawing | Google's SigLIP (base, 224 px), zero-shot against photo and drawing descriptions | Apache-2.0 |
| Faces | OpenCV Zoo's YuNet | MIT |
| Apparent age, photos | Two models on six cuts of each face: SigLIP 2 fine-tuned on age bands, and Civitai's age-vit | Apache-2.0 |
| Apparent age, drawings | SmilingWolf's wd-vit-tagger-v3 (its child tags) | Apache-2.0 |
All open models, run on our servers in Zurich, pinned to the exact files we tested.
Error rates
Share of real photos whose face was read as a minor, measured in October 2026 with these models and lines on FairFace's validation photos (5,980 faces):
| Age | Explicit picture | Suggestive picture |
|---|---|---|
| 0–2 | 98.5% | 97% |
| 3–9 | 92% | 85% |
| 10–19 | 47% | 34% |
| 20–29 | 5.9% | 2.3% |
| 30 and over | 0.9% | 0.6% |
Adults read as minors in explicit pictures are not spread evenly: in their twenties, women 8.0% and men 3.2%; Black and Southeast Asian faces about 12.5%, White faces 1.7%. FairFace is one model's own training set, so real pictures will do worse. On everyday pictures without nudity, the check raised no false alarm in 78 under either policy (the robustness report). Known limits:
- a face it can't find (turned away, hidden, or under 32 px tall) gets no age read, so such a photo passes;
- apparent age is a guess near 18, both ways: most older teens pass;
- a near-photoreal 3D render counts as a photo;
- the tagger reads anime-style drawings best; other styles may hide a drawn child.
Privacy
The picture is read in memory and never kept: no copy, no score, no hash. The answer goes only to you, never into a file's marks.
Errors
| Status | Code | When |
|---|---|---|
| 400 | invalid_request | No file, a field sent twice, a policy other than ai-act or safe-only, or a picture that can't be read. |
| 400 | unknown_field | A field other than file (or upload) and policy. |
| 400 | option_not_available | url, categorize or describe: not available yet. |
| 401 | missing_api_key, invalid_api_key, api_key_revoked | See authentication. |
| 402 | quota_exceeded | Fewer than two marks left this month (24 for a clip). |
| 403 | insufficient_scope, ip_not_allowed | The key lacks the check scope, or the address isn't allowlisted. |
| 404 | upload_not_found | The upload named isn't one this key can use: unknown, used, expired, or another key's. |
| 413 | payload_too_large | The request is over 4 MB (send larger files through an upload), or a clip over 25 MB or our limits. |
| 415 | unsupported_media_type | Not a PNG, JPEG or WebP picture or an MP4 clip, or not sent as multipart/form-data. |
| 429 | rate_limited | Too many calls this minute; retry after Retry-After seconds. |
| 503 | marker_unavailable, storage_unavailable | The check (or uploads) can't run right now. It never skips: nothing is charged; retry. |
Error bodies follow the common format.