POST
/api/v1/text
Moderate text
Comments, reviews, messages, descriptions. The endpoint most integrations use.
The endpoint most integrations use. Send a comment, a review, a message or a product description, and get back what is wrong with it and how sure we are.
Parameters
| Field | Type | Required | |
|---|---|---|---|
content | string | yes | The text to judge. Up to 20,000 characters: a long forum post and a short novel. The cap is there because everything downstream is linear in length. |
locales | string[] | no | The languages your site is in, up to ten (["es", "ca"]). Without it, a fluent wall of text in the wrong language cannot be told from a normal comment. It does not select the word lists; see languages. |
surface | string | no | Where it was posted. Your own label, and three of them switch a check on: see surfaces. |
ai | boolean | no | Default true. Set false to stay on the cheap detectors and never spend a model call. |
reference | string | no | Your own id for the thing being judged, up to 120 characters. Never parsed, stored and handed back, so a verdict is findable later by the name you already use for it. |
policy | string | no | Which of your policies to judge under. Absent means the project's policy, then your default; an account with none gets the shipped thresholds. |
project | string | no | Which of your projects the verdict is filed under. Absent means your default project. |
actor | string | no | Your own id for whoever wrote it. Filed with the verdict, and used to move the line when a policy asks for reputation. |
redact | boolean | no | Hand back redacted: the same text with the personal data masked out. For a chat or a marketplace that is usually worth more than refusing the message. |
Request
curl https://toxicfilter.com/api/v1/text \
-H "Authorization: Bearer tf_live_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"content": "Buy cheap followers now!!! www.example-spam.tld",
"locales": ["en"],
"surface": "comment"
}'
Response
{
"id": "mod_01jr7q9x2c8h4m6v0b3n5k7t9d",
"reference": null,
"decision": "block",
"flagged": ["spam"],
"scores": { "spam": 0.91 },
"signals": [
{
"category": "spam",
"score": 0.91,
"detector": "link_spam",
"reason": "Promotional link with no surrounding context.",
"evidence": ["www.example-spam.tld"]
}
],
"used_ai": false,
"took_ms": 4,
"credits": { "remaining": 4931, "renews_at": "2026-09-30T00:00:00+00:00" }
}
Every field is explained in The response.
Surfaces, and the three that switch a check on
surface is your own label for where the content sits, and a
policy can carry a different line
per surface: a link to a shop is the entire point of a marketplace listing and is spam
in a support thread. Send whatever names your own product uses, such as
comment, bio, listing, message or
photo.
Three names mean something to us as well, because three checks would be unusable on ordinary traffic and are gated on them. Nothing else about the surface is interpreted:
surface | What it switches on |
|---|---|
prompt |
prompt_injection. In a forum comment ignore everything above is somebody being odd; on its way into a model it is an instruction. /v1/prompt sets it for you. |
review |
Fake reviews, under spam: a review that was paid for, or written by the business about itself. On a comment it would fire on anybody quoting a review policy, and our hotel room is what a guest writes. |
job |
Discriminatory job ads, under hate: an ad that excludes candidates by sex, age, origin, religion, family or disability. Gated because only women in a shared flat advert and only men about a choir are not job ads. It reviews and never blocks by default, since genuine occupational requirements exist and the words cannot tell them apart. |
None of the three is on unless you send the name. A review site sending
"surface": "reviews"or nothing at all gets no fake-review check, and nothing in the answer says so, which is why it is written down here. Match the string exactly.
When to turn ai off
The cheap detectors match shapes: links, alphabets, repetition, contact details, known terms. They settle most traffic on their own, and the model only runs when they leave the question open. That escalation is what catches sarcasm, a threat with no threatening word in it, and an insult that is technically a compliment.
Turn it off when you want a hard cost ceiling, or on a high-volume surface where a miss is cheap. Leave it on everywhere else.
ai: falseis not a faster version of the same answer. It is a different, blunter answer that cannot read meaning. Checkused_aiin the response if you need to know which one you got.