# Moderations

<aside class="warn"><strong>Warning</strong> If no moderation model is available, every request to this endpoint returns <code>400</code>. Check <code>GET /v1/models?modality=moderation</code> before you call it.</aside>

Classify text and learn whether it is flagged, with a score for each category. The endpoint is compatible with the OpenAI Moderations API.

`POST /v1/moderations`

Only moderation models are served here. List them with a public call that needs no key:

```bash
curl "https://kenari.id/v1/models?modality=moderation"
```

Otherwise the contract below applies to the ids it returns. A plain `GET /v1/models` does not list embedding, rerank, moderation or System One models. It does list chat, image, speech, transcription and video models.

## Request

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `model` | string | yes | A moderation model id from the call above. |
| `input` | string or array of strings | yes | One text, or several texts in one request. Every text must be a non-empty string. Up to 2,048 texts and 1 MiB of text in total. |

## Response

The response has the OpenAI shape. `id` starts with `modr-`, `model` is the id you requested, and `results` has one entry per input, in the same order. Each entry has `flagged`, `categories` (a boolean per category) and `category_scores` (a number per category). Only the standard OpenAI category names are returned. `<model-id>` below stands for an id returned by the models call.

```json
{
  "id": "modr-...",
  "model": "<model-id>",
  "results": [
    {
      "flagged": false,
      "categories": { "harassment": false, "hate": false },
      "category_scores": { "harassment": 0.001, "hate": 0.0002 }
    }
  ]
}
```

## Examples

Replace `<model-id>` with an id from `GET /v1/models?modality=moderation`.

### curl

```bash
curl https://kenari.id/v1/moderations \
  -H "Authorization: Bearer $KENARI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "<model-id>",
    "input": "text to check"
  }'
```

### Python

```python
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://kenari.id/v1",
    api_key=os.environ["KENARI_API_KEY"],
)

result = client.moderations.create(
    model="<model-id>",
    input="text to check",
)

print(result.results[0].flagged)
```

### JavaScript

```js
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://kenari.id/v1",
  apiKey: process.env.KENARI_API_KEY,
});

const result = await client.moderations.create({
  model: "<model-id>",
  input: "text to check",
});

console.log(result.results[0].flagged);
```

## Billing

Moderation is billed per input token. Moderation models are free by default, so a model without a price set charges Rp 0. The price of each model is in its catalog entry. See [How billing works](/en/docs/billing) for balance and price dimensions.

## Errors

| Status | Code | When |
| --- | --- | --- |
| 400 | `bad_request` | `model` is unknown or is not a moderation model (the message lists the moderation ids that are available), `input` is missing or contains an empty or non-string item, or the request is over the 2,048 text or 1 MiB limit. |
| 400 | `model_not_found` | The model is listed but no provider serves it right now. Retry later. |
| 503 | `upstream_error`, `all_providers_failed` | No provider returned a usable response. You are not charged. Retry. |

See [Errors](/en/docs/errors) for the other codes.

## See also

- [Models and pricing](/en/docs/models)
- [Chat completions](/en/docs/chat)
- [How billing works](/en/docs/billing)
