Documentation
ModAPI moderates text and images over a small set of JSON/multipart endpoints. Every check returns a per-category confidence score from 0.00 to 1.00, so you decide where the line is.
Base URL:
https://api.modapi.xyz
Authentication
Send your API key as a bearer token on every request:
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxx
Each key has its own moderation toggles, set from Dashboard → API Keys → Settings: text, image, scam, context, and go_over_billing. A call to an endpoint whose toggle is off returns 403.
Text moderation
POST /v1/moderate/text
Scores a message across toxic, harassment, hate, and scam. Pass up to 5 prior messages in context for conversation-aware scoring — requires the key's context toggle.
curl -X POST https://api.modapi.xyz/v1/moderate/text \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "message": "send btc now to claim your prize", "context": ["hey are you free today?"] }'
{ "flagged": true, "confidence": { "scam": 0.90, "toxic": 0.00, "harassment": 0.00, "hate": 0.00 }, "hash": "9f86d081884c7d65...2f4b2d", "cached": false, "latency_ms": 42, "route": "/v1/moderate/text" }
Image NSFW detection
POST /v1/moderate/image
Multipart upload, field name file. Scores nudity signals from the image, up to 10MB. Requires the key's image toggle.
curl -X POST https://api.modapi.xyz/v1/moderate/image \ -H "Authorization: Bearer $API_KEY" \ -F "[email protected]"
{ "flagged": false, "confidence": { "nudity": 0.12 }, "hash": "e3b0c44298fc1c14...7852b855", "cached": true, "latency_ms": 3, "route": "/v1/moderate/image" }
Scam image detection
POST /v1/moderate/image/scam
Multipart upload, field name file. Runs OCR on the image and scores the extracted text the same way as text moderation. Requires the key's scam toggle.
Optionally request extra signal checks via the signals form field — off by default, additive only:
qr_code— decodes QR codes in the image and scores their contentphone_number— flags embedded phone numbers as a scam signal
curl -X POST https://api.modapi.xyz/v1/moderate/image/scam \ -H "Authorization: Bearer $API_KEY" \ -F "[email protected]" \ -F "signals=qr_code" -F "signals=phone_number"
The cache key includes which signals were requested, so a plain scan and a signal-boosted scan of the same image are cached separately.
Hash lookup
GET /v1/moderate/{category}/hash/{hash}
Check whether a hash already has a cached result — without re-sending the message or re-uploading the image. Every moderation response returns a hash field; pass it back here to see if (and how) that content was scored, paying nothing for the model.
category is one of text, image, or scam, and requires that key toggle. hash is the 64-character hex SHA-256 digest from a prior response. Text and text-with-context hashes are both looked up under text.
curl https://api.modapi.xyz/v1/moderate/text/hash/9f86d081884c7d65...2f4b2d \ -H "Authorization: Bearer $API_KEY"
{ "category": "text", "hash": "9f86d081884c7d65...2f4b2d", "present": true, "confidence": { "scam": 0.90, "toxic": 0.00, "harassment": 0.00, "hate": 0.00 }, "latency_ms": 2, "route": "/v1/moderate/text/hash" }
If the hash isn't cached, present is false and confidence is null. A miss is not an error — it just means nothing has been scored under that hash yet. Note that lookups never run a model, so they don't reflect any rules, which are applied per request at moderation time.
Because a lookup is just an indexed cache read, it gets its own rate-limit budget at 5× your plan's per-minute limit, counted separately from moderation calls — so checking the cache before you submit never eats into your scoring quota.
Response format
The three moderation endpoints return the same shape:
{ "flagged": boolean, "confidence": { category: 0.00–1.00, ... }, "hash": string, "cached": boolean, "latency_ms": integer, "route": string, "annotations": { ... } // only when a rule adds one }
flagged is true if any category in confidence crosses its threshold (default 0.70), unless one of your rules overrides it. Categories with no signal are 0.00, not omitted. hash is the SHA-256 digest of the request — the same value you pass to hash lookup. annotations is present only when an annotate rule matched.
Rules & annotations
Rules let a key reshape its own results without you touching thresholds in code. Each rule tests one category against a threshold and takes an action when it matches. Manage them from Dashboard → Rules; how many rules a key may have depends on its plan.
- category —
toxic,harassment,hate,scam,nudity, orany(the highest score in the response). - comparator —
gte(score ≥ threshold) orlt(score < threshold). - action —
flagforcesflagged: true,unflagforcesfalse, andannotateattaches a custom key/value underannotations. - scope — restrict a rule to
text,image, orscam, or apply it toallendpoints.
Rules run in priority order after scoring; for flag/unflag, the last matching rule wins. Annotation keys are namespaced under annotations, so a rule can never overwrite a reserved field like flagged or confidence.
Caching & hashing
Every request is hashed and checked against a result cache before any model runs:
- Text — SHA-256 of the normalized message, plus context if provided (context changes the result, so it's part of the key).
- Images — SHA-256 of the raw bytes, plus requested
signalsfor the scam endpoint. NSFW and scam checks on the same image are cached separately.
A cache hit skips OCR, the nudity model, and the LLM call entirely — you get the prior result back in a few milliseconds, marked "cached": true.
Regex fast-path
Before any model runs, text is checked against curated regex patterns per category. A high-confidence regex match (≥ 0.90) short-circuits the request — no LLM call, no added latency. Ambiguous text falls through to the language model for nuance.
Plans & rate limits
Every API key bills against either a personal plan or a workspace plan, never both:
- A key with no workspace, or one tagged to a workspace with no active subscription, draws on the key owner's personal plan (
free,developer, orpro). - A key tagged to a workspace that has an active
teamorenterprisesubscription draws on that workspace's plan instead — shared across everyone who creates keys under it.
| Plan | Scope | Active keys | Requests / min | Price |
|---|---|---|---|---|
| Free | Personal | 2 | 25 | €0 |
| Developer | Personal | 5 | 200 | €4.99/mo |
| Pro | Personal | 10 | 600 | €9.99/mo |
| Team | Workspace | 20 | 2000 | €29.99/mo |
| Enterprise | Workspace | Custom | Custom | Contact us |
Upgrade or manage billing from Dashboard → Billing, via Stripe or PayPal. Enterprise is provisioned manually after a sales conversation. Personal and workspace key quotas are counted independently, so a workspace's Team or Enterprise plan never raises (or lowers) the cap on your own personal keys, and vice versa.
Errors
| Status | Meaning |
|---|---|
| 401 | Missing, invalid, or revoked API key. |
| 403 | That moderation type is disabled for this key, or context was sent without the context toggle enabled. |
| 404 | Unknown hash-lookup category (must be text, image, or scam). |
| 413 | Image larger than 10MB. |
| 422 | Request failed validation (e.g. message empty, more than 5 context messages, or a malformed lookup hash). |
| 429 | Rate limit exceeded for the key's plan (see Plans & rate limits). Upgrade for a higher limit. |
ModAPI