> ## Documentation Index
> Fetch the complete documentation index at: https://docs.inputapi.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Content moderation

> Read text moderation decisions and handle failure and review.

A moderation model requires separate deployment support. Use the [Moderations API](/en/api-reference/moderations.md) to check text; conversational refusals and gateway keyword rules are separate mechanisms. Set an explicit moderation model ID instead of relying on the gateway's historical default.

## Run batch text moderation

Install dependencies in the documentation directory using the [Python guide](/en/guides/python.md), then replace the address, token, and moderation model. The script submits two fixed texts and prints `allow` or `review` in input order without printing source text or scores.

```sh theme={null}
export API_BASE_URL="https://gateway.example.com/v1"
export API_KEY="YOUR_API_KEY"
export API_MODEL="YOUR_MODEL_ID"
.venv/bin/python examples/moderations.py
```

`examples/moderations.py` uses the SDK's generic HTTP method to read raw dictionaries so missing fields or string booleans cannot become valid decisions.

```python theme={null}
import math
import os
from openai import OpenAI, APIConnectionError, APIStatusError

texts = ["A friendly greeting.", "An invitation to a community picnic."]
with OpenAI(base_url=os.environ["API_BASE_URL"].rstrip("/"),
            api_key=os.environ["API_KEY"], timeout=60.0, max_retries=0) as client:
    try:
        result = client.post("/moderations", cast_to=dict, body={
            "model": os.environ["API_MODEL"], "input": texts,
        })
        if not isinstance(result, dict) or result.get("error"):
            raise ValueError("Invalid moderation response.")
        rows = result.get("results")
        if not isinstance(rows, list) or len(rows) != len(texts):
            raise ValueError("Missing moderation results.")
        decisions = []
        for row in rows:
            if not isinstance(row, dict) or type(row.get("flagged")) is not bool:
                raise ValueError("Missing moderation decision.")
            categories, scores = row.get("categories"), row.get("category_scores")
            if not isinstance(categories, dict) or not categories or not isinstance(scores, dict):
                raise ValueError("Missing moderation categories.")
            if categories.keys() != scores.keys():
                raise ValueError("Category keys do not match.")
            if any(type(value) is not bool for value in categories.values()):
                raise ValueError("Invalid category decision.")
            if any(type(value) not in (int, float) or not math.isfinite(value)
                   or not 0 <= value <= 1 for value in scores.values()):
                raise ValueError("Invalid moderation score.")
            # This application sends any positive signal to review.
            decisions.append("review" if row["flagged"] or any(categories.values()) else "allow")
        for index, decision in enumerate(decisions):
            print(f"{index}: {decision}")
    except APIStatusError as error:
        raise SystemExit(f"Request failed (HTTP {error.status_code}). No automatic retry.")
    except APIConnectionError:
        raise SystemExit("Connection failed. No moderation decision available.")
    except (ValueError, TypeError, AttributeError):
        raise SystemExit("Invalid moderation response. No moderation decision available.")
```

## Apply the result

* `results` follows input order; there is no document index to replace that position. The example checks the count and validates every result before printing decisions.
* `flagged` is the overall signal, `categories` contains category signals, and `category_scores` contains confidence scores. The sample application routes any positive signal to review; `allow` is not proof of safety.
* Categories can evolve with the model, so the example does not freeze an enum. Evaluate thresholds for your language, use case, and model instead of copying thresholds across models. See the [OpenAI moderation guide](https://developers.openai.com/api/docs/guides/moderation) for field semantics.
* HTTP errors, empty results, and invalid scores mean no usable decision. They do not mean approval. The script exits unsuccessfully; the application must choose review, delayed publication, or a retry message.

## Check inputs and outputs

Check user input before deciding whether to invoke generation, and check generated content before publication. If a workflow requires reviewing the complete output, buffer it on the server before publishing. Do not assume streamed content already shown to a user can be recalled.

Image moderation requires separate confirmation of the model, channel, and content block format. This guide and example cover only text and text batches. Moderation does not replace permissions, tool execution authorization, or RAG document access controls.

## Gateway adaptation caveat

The current OpenAI adapter can rebuild a response as a generic text response when the channel enables `ForceFormat`, losing moderation-specific `results`. For HTTP 200 without results, ask the administrator to inspect channel response formatting and the original upstream structure. A registered route does not guarantee every channel preserves moderation fields.

[Errors and retries](/en/guides/errors.md) · [Production integration](/en/guides/production.md)

[阅读中文](/zh/guides/moderations.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.