API Reference

๐Ÿ›ก๏ธ image-guard

Copyright & NSFW image detection API โ€” v1.1.0  ยท  โ† Demo page

Introduction

image-guard is a REST API that detects copyright-protected content (characters, brand logos, trademarks) and NSFW content in images. Results are streamed in real-time via Server-Sent Events (SSE).

๐Ÿ“Œ Base URL: https://image-guard.readingtester.com

Authentication

All requests to /api/* require an API key passed as a request header:

HeaderValue
X-API-KeyYour API key

Requests without a valid key return 401 Unauthorized.

How it works

Detection runs in two stages:

StageModelPurposeAvg time
Stage 1 โ€” Vision GPT-4.1 (1024px, detail:high) Analyze image for copyright content, NSFW, and generate an edit prompt ~1s
Stage 2 โ€” Edit check OpenAI gpt-image-1 + Gemini 2.5-flash (parallel) Attempt image edit โ€” content policy block = copyright/NSFW confirmed ~30s

Early exit: if Stage 1 detects copyright or NSFW, Stage 2 is skipped. The full pipeline only runs for images that pass Stage 1.

GET /health

GET/health
Check if the service is running. No authentication required.

Response

"status": "ok",
"version": "1.1.0"

POST /api/check

POST/api/check
Upload an image for copyright and NSFW detection. Returns an SSE stream.
โšก This endpoint streams Server-Sent Events โ€” not a standard JSON response. You must consume the stream to get the final result. See SSE Events below.

Request

FieldTypeRequiredDescription
image file required Image file โ€” multipart/form-data. JPEG, PNG, WEBP. Max 20MB.
HeaderRequiredValue
X-API-KeyrequiredYour API key
Content-Typeautomultipart/form-data (set by HTTP client)
Acceptoptionaltext/event-stream

Response

Content-Type: text/event-stream. Each event is a JSON object prefixed with data: .

SSE Events

Events arrive in order. The final event is always result.

vision Stage 1 analysis result (always first)
{
  "type": "vision",
  "copyright": true,          // boolean โ€” copyright content detected
  "identified": "Mickey Mouse (Disney)",  // string โ€” what was found, or null
  "nsfw": false,               // boolean โ€” NSFW content detected
  "nsfwReason": null,           // string | null
  "editPrompt": null            // string | null โ€” only set if image is safe
}
provider_result Stage 2 result per provider (only if Stage 1 clean)
{
  "type": "provider_result",
  "provider": "openai",        // "openai" | "gemini"
  "blocked": false             // true = content policy blocked = flagged
}
result Final verdict (always last)
{
  "type": "result",
  "safe": false,               // true = safe to use, false = flagged
  "reason": "copyright",        // "copyright" | "nsfw" | "provider" | null
  "identified": "Mickey Mouse (Disney)",
  "earlyExit": true             // true = Stage 2 was skipped
}

Complete flow โ€” FLAGGED (early exit)

data: {"type":"vision","copyright":true,"identified":"Mickey Mouse (Disney)","nsfw":false,"nsfwReason":null,"editPrompt":null}

data: {"type":"result","safe":false,"reason":"copyright","identified":"Mickey Mouse (Disney)","earlyExit":true}

Complete flow โ€” SAFE (full pipeline)

data: {"type":"vision","copyright":false,"identified":null,"nsfw":false,"nsfwReason":null,"editPrompt":"Replace the dog with a cat"}

data: {"type":"provider_result","provider":"openai","blocked":false}

data: {"type":"provider_result","provider":"gemini","blocked":false}

data: {"type":"result","safe":true,"reason":null,"identified":null,"earlyExit":false}

Error Responses

HTTP StatusConditionBody
400No image uploaded{"error": "No image provided..."}
401Missing or invalid API key{"error": "Unauthorized..."}
413File exceeds 20MB{"error": "File too large"}
500Internal errorSSE {"type":"error","message":"..."}

Code Examples

cURL

curl -X POST https://image-guard.readingtester.com/api/check \
  -H "X-API-Key: YOUR_API_KEY" \
  -F "image=@/path/to/image.jpg" \
  --no-buffer

JavaScript (Node.js / fetch)

import fs from "fs";

const form = new FormData();
form.append("image", new Blob([fs.readFileSync("image.jpg")]), "image.jpg");

const res = await fetch("https://image-guard.readingtester.com/api/check", {
  method: "POST",
  headers: { "X-API-Key": "YOUR_API_KEY" },
  body: form,
});

const reader = res.body.getReader();
const decoder = new TextDecoder();

while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  const lines = decoder.decode(value).split("\n");
  for (const line of lines) {
    if (line.startsWith("data: ")) {
      const event = JSON.parse(line.slice(6));
      console.log(event);
      if (event.type === "result") {
        console.log("Safe:", event.safe, "| Reason:", event.reason);
      }
    }
  }
}

Python

import requests

with open("image.jpg", "rb") as f:
    response = requests.post(
        "https://image-guard.readingtester.com/api/check",
        headers={"X-API-Key": "YOUR_API_KEY"},
        files={"image": ("image.jpg", f, "image/jpeg")},
        stream=True,
    )

for line in response.iter_lines():
    if line.startswith(b"data: "):
        import json
        event = json.loads(line[6:])
        print(event)
        if event["type"] == "result":
            print("Safe:", event["safe"])

Limits & Notes

LimitValue
Max file size20 MB
Supported formatsJPEG, PNG, WEBP, GIF
Vision resolutionResized to 1024px max (detail:high โ€” tiles image for accuracy)
Edit API resolutionResized to 768px max
Detection thresholdReliable down to ~4% image size (~40px); below 2% is below perceptual limit
Stage 2 timeout90s per provider
Response formatSSE stream (text/event-stream)

Provider Cost Comparison

Estimated cost per image check (approx. 500 input + 150 output tokens):

ProviderModelInput $/MTokOutput $/MTokCost/CheckCost/1K checks
openaigpt-4.1$2.00$8.00~$0.0022~$2.20
geminigemini-2.5-flash$0.15$0.60~$0.00017~$0.17
claudeclaude-haiku-4-5$0.80$4.00~$0.001~$1.00
grokgrok-4-0709$3.00$15.00~$0.0042~$4.20

Recommendation: Default providers=openai,gemini gives best speed+cost balance. GPT-4.1 is fastest (~1s) for primary detection; Gemini 2.5-flash is 13x cheaper for secondary confirmation.