Analyze endpoint
POST /v1/analyze — inputs (URL, base64, multipart), options (max_frames, include_frames, policy) and the full response.
POST https://api.sfwapi.dev/v1/analyze
Input
Exactly one media source per request:
| How | Body | Notes |
|---|---|---|
| URL | {"url": "https://…"} | Public http(s) URL. Our servers download it; redirects are followed up to 3 hops; private and local addresses are refused. |
| Upload | multipart field file | Preferred for files you already hold. The file name you send is discarded. |
| Base64 | {"base64": "…"} | Convenient from a canvas; costs about a third more bandwidth than an upload. A data: prefix is stripped. |
Options (JSON fields, or form fields on multipart):
| Field | Type | Default | Meaning |
|---|---|---|---|
max_frames | 1–32 | 5 for GIF, 10 for video | Frames sampled evenly over the clip (first and last always included). One credit per analysed frame. |
include_frames | boolean | false | Return frames[] with per-frame scores and levels. |
policy | object | none | See Policies. On multipart, send it as a JSON string. |
Accepted media: JPEG, PNG, WebP, GIF, BMP, TIFF, AVIF, HEIC, MP4, MOV, WebM, MKV, AVI. Size limits depend on your tier (free: 8 MB, no video; paid: 32 MB uploads, 32 MB images, 64 MB GIFs and videos by URL). Sync requests time out after 60 seconds.
Response
{
"id": "an_01JD4Q0K7X3Y9Z8W2V1U0T9S8R",
"media": { "kind": "gif", "width": 480, "height": 270, "frames_analyzed": 5, "duration_ms": 2400 },
"nudity": { "level": 2, "scores": { "revealing": 96.2, "implied_nudity": 71.5, "explicit": 8.1 } },
"flags": { "art": { "score": 3.4, "detected": false }, "sex_object": { "score": 0.9, "detected": false } },
"policy": { "blocked": true, "reason": "level", "matched_level": 2 },
"frames": [ { "index": 0, "timestamp_ms": 0, "level": 0, "scores": { "…": 0 }, "flags": { "…": 0 } } ],
"usage": { "credits": 5, "balance": 9995 },
"timing_ms": { "upstream": 1840, "total": 1862 }
}nudity.level: highest level whose score is above 50 in any frame.nudity.scores: maximum over frames per level threshold.flags.*.detectedisscore > 50.policyappears only when a policy was sent;reasonislevel,sex_objectorart_level.framesappears only withinclude_frames: true.
Headers: X-Request-Id (quote it to support), X-Credits-Remaining, RateLimit, RateLimit-Policy, Retry-After on 429/503.
The machine-readable contract is at /openapi.json; an interactive reference lives at api.sfwapi.dev/reference.