All pages

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

FieldTypeRequired
contentstringyes 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.
localesstring[]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.
surfacestringno Where it was posted. Your own label, and three of them switch a check on: see surfaces.
aibooleanno Default true. Set false to stay on the cheap detectors and never spend a model call.
referencestringno 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.
policystringno Which of your policies to judge under. Absent means the project's policy, then your default; an account with none gets the shipped thresholds.
projectstringno Which of your projects the verdict is filed under. Absent means your default project.
actorstringno Your own id for whoever wrote it. Filed with the verdict, and used to move the line when a policy asks for reputation.
redactbooleanno 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:

surfaceWhat 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: false is not a faster version of the same answer. It is a different, blunter answer that cannot read meaning. Check used_ai in the response if you need to know which one you got.