NSFW Moderation

nsfw.sptrnd.id

Try it
Drag & drop an image here, or click to browse
JPEG / PNG / WebP · max 10 MB · dimensions ≤ 8000 px
Rate limit
  • 120 requests / minute per IP, counted with a sliding window log (accurate at window boundaries, no 2× burst).
  • Exceeding the quota returns 429 RATE_LIMITED with a Retry-After header (seconds until a slot frees up).
  • Rejected requests don't read the body, so a 10 MB upload isn't wasted.
  • Every response includes RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset headers.
Model
  • Falconsai/nsfw_image_detection, a fine-tuned ViT-base image classifier.
  • Two labels: nsfw and normal, each response includes a confidence score on the chosen label.
Usage

How it works

Send an image. Multipart field image or a raw body with Content-Type: image/*.
Read the verdict. The JSON response carries label, is_nsfw and confidence.
Stay in budget. 120 requests/minute per IP; on 429 wait for Retry-After seconds.

Endpoint

Method & path Description
POST /v1/moderate Multipart field image, or a raw body with Content-Type: image/*. Rate-limited.

curl

curl -X POST --data-binary @photo.jpg \
  -H "Content-Type: image/jpeg" \
  https://nsfw.sptrnd.id/v1/moderate

Or multipart: curl -F image=@photo.jpg https://nsfw.sptrnd.id/v1/moderate

JavaScript

const r = await fetch("https://nsfw.sptrnd.id/v1/moderate", {
  method: "POST",
  headers: { "Content-Type": "image/jpeg" },
  body: file, // a File or Blob
});
const { result } = await r.json();

Python

import requests

r = requests.post(
    "https://nsfw.sptrnd.id/v1/moderate",
    headers={"Content-Type": "image/jpeg"},
    data=open("photo.jpg", "rb").read(),
)
print(r.json()["result"]["label"])

Success response (3 keys)

{
  "request_id": "0192f0c4-...",
  "image":   { "sha256": "...", "size_bytes": 183422,
               "mime": "image/jpeg", "width": 1024, "height": 768 },
  "result":  { "label": "nsfw", "is_nsfw": true, "confidence": 0.9871 }
}

confidence is the certainty of the chosen label (4 decimals). Repeating the same image returns an identical body, just faster.

Response fields

request_idunique id for tracing; keep it when reporting an issue
image.sha256hash of the uploaded file
image.size_bytes, mime, width, heightwhat the server actually received and how it was read
result.labelnsfw or normal
result.is_nsfwthe moderation decision as a boolean
result.confidencecertainty of the label, 0 to 1

Errors

All errors are returned as application/problem+json:

{ "error": { "code": "PAYLOAD_TOO_LARGE",
  "message": "payload exceeds 10 MB", "request_id": "..." } }
RATE_LIMITEDquota exceeded; wait Retry-After seconds, then retry
PAYLOAD_TOO_LARGEfile is over 10 MB; compress or resize it
UNSUPPORTED_MEDIA_TYPEonly JPEG, PNG and WebP are accepted
IMAGE_TOO_LARGEdimensions exceed 8000 px; downscale the image
TIMEOUT, WORKER_UNAVAILABLEservice is busy; retry with backoff

Other codes: INVALID_REQUEST · EMPTY_BODY · OVERLOADED · INTERNAL

SLA (best effort)
  • Best effort only. The service is operated without any uptime guarantee or SLA. It may be unavailable at any time for maintenance, failures, or capacity reasons.
  • No response-time commitment. Temporary outages do not entitle you to compensation, and there is no support response-time guarantee.
  • Load dependent. Under heavy load requests can be rejected with 429 or 503; retry politely using the Retry-After header.
  • Best-effort caching. Cached verdicts may be evicted at any time and recomputed on the next request.
Privacy
  • Uploads are transient. Your image is written to a private temp folder, used only for the single check you requested, and deleted immediately afterwards.
  • Orphan cleanup. If anything goes wrong mid-processing, leftover temp files are removed automatically by a cleanup job within minutes.
  • Nothing is kept or shared. We do not store, share, or train on your images. Only the verdict scores are cached, keyed by the file's hash and never the image itself.
  • Logs contain metadata only. Request id, file hash, size, verdict and timing. Image content is never logged or stored.
  • No accounts. Registration is not required. Rate limiting uses your IP address, kept in memory only for a short window.