Detect NSFW content
Every platform that lets strangers upload images eventually needs a filter between 'submitted' and 'live'. This endpoint screens each image for adult and explicit content and hands back a clear signal — safe, flagged or explicit — before it ever reaches another user's screen.
Run it online
Run this on our servers with your account. Free tools run in your browser; this one bills your KIT balance per the price above.
The moment moderation becomes non-optional
A dating app, a forum, a marketplace with user photos, a comment section that accepts images — all of them are one bad upload away from a support ticket, a press problem or a policy violation. Manual review doesn't scale past a small volume, and by the time a human flags something, it may already have been seen. image.moderate exists to sit in that gap, screening every image automatically before publication rather than after a complaint.
What the check actually returns
POST an image to /image/moderate and get back a moderation verdict describing the likelihood of adult or explicit content, so your application can set its own threshold: auto-approve clearly safe uploads, auto-reject clearly explicit ones, and route the gray area to a human moderator. The decision of where those lines sit stays entirely with you — the API supplies the signal, not the policy.
Screening at the speed uploads actually arrive
Uploads rarely come one at a time on a predictable schedule; they spike whenever traffic spikes. The endpoint is asynchronous for exactly that reason — it accepts the image, returns a task_id immediately so your upload flow never stalls, and delivers the verdict moments later by webhook, or via a signed link you can retrieve for 24 hours.
A layer, not a full moderation policy
Automated NSFW detection is a well-established first line of defense in content moderation, but it's deliberately narrow: it flags a specific category of content, not every kind of harmful upload. Most production systems pair it with other checks — profanity filters on captions, rate limiting on new accounts, human review of anything above a certain risk score — rather than treating any single signal as the whole moderation stack.
Fitting it into an upload pipeline
The common pattern is to call image.moderate immediately after an upload lands in storage and before it's shown to any other user, holding the image in a pending state until the verdict returns. Combine it with image.quality to also reject broken or unusable files at the same step, so a single moderation gate handles both safety and basic technical hygiene. Pricing is a small fixed fee per request plus a per-image rate, both published; failed checks retry automatically up to three times and are never billed if they still fail.
What you can do with it
Dating and social apps
Screen every profile photo automatically before it becomes visible to other users, cutting exposure to explicit uploads to near zero.
Marketplaces with user photos
Moderate listing images at upload time so sellers can't publish explicit content in place of the product they're supposed to show.
Community forums and comments
Check images attached to posts or comments before they render publicly, instead of relying only on user reports after the fact.
User-generated content platforms
Run every upload through moderation at ingestion, well before it reaches a content feed or recommendation system.
FAQ
How accurate is the NSFW detection API?
It returns a likelihood-based verdict rather than a guaranteed judgment, so most teams set their own threshold and route borderline cases to human review.
Is image moderation free to use?
There's no free tier — free tiers get abused and slow everyone down. Access runs on a prepaid ForHosting KIT balance: top up from $10.00 (it never expires) and each request is charged at its published price, so a call with no balance returns HTTP 402. No subscription, no tokens, no invented credits, and a failed task is never charged.
What does the moderation result include?
A verdict indicating the likelihood of adult or explicit content, which your application can map to allow, block or manual-review actions.
How is image.moderate priced?
A small base fee per request plus a per-image rate, both published on this page, with no invented tokens or credits.
Can this replace a human moderation team?
It replaces the first pass, not the whole process — most platforms still route flagged or borderline images to a human before a final decision.
Can I moderate images in bulk before a migration?
Yes, each image is an independent async task, so a backlog of existing images can be screened the same way as live uploads.
How fast do moderation results come back?
Each request returns a task_id immediately, and the verdict follows by signed webhook, or a signed link valid for 24 hours.
What happens if a moderation check fails?
It's retried automatically up to three times; if it still fails you get a clear error and are not charged for that request.
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, and soon from our app, email and Telegram.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/image/moderate \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"image":"https://ejemplo.com/imagen.jpg"}'const res = await fetch("https://api.kit.forhosting.com/image/moderate", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"image": "https://ejemplo.com/imagen.jpg"
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/image/moderate",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"image": "https://ejemplo.com/imagen.jpg"
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/image/moderate", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"image":"https://ejemplo.com/imagen.jpg"}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"image":"https://ejemplo.com/imagen.jpg"}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/image/moderate", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"image": "https://ejemplo.com/imagen.jpg"
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "image.moderate",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Limits
max_mb | 15 |
max_megapixels | 12 |
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |
422 | task_failed | The task failed after 3 retries. You are never charged for it. |