> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.beatapi.io/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.beatapi.io/_mcp/server.

# Moderation API

> 1 件のテキストを 7 種類のコンテンツカテゴリで判定し、カテゴリ別スコアと allow・review・block の判定結果を取得します。リクエストとレスポンスは OpenAI Moderation API の形式に対応しています。

# Moderation API

テキストを 1 件送信すると、その後の処理に使える判定結果が返ります。7 カテゴリそれぞれの `0`～`1` のスコア、各スコアがしきい値に達したかどうか、そして `allow`・`review`・`block` のいずれかの総合判定です。ユーザーやモデルが作成したテキストを対象に、コメントの公開前、プロンプトのモデル送信前、回答のユーザーへの表示前などの確認に利用できます。

`POST https://api.beatapi.io/v1/moderations`

リクエストとレスポンスは OpenAI Moderation API に準拠しているため、公式 OpenAI SDK はベース URL と API キーを変更するだけで利用できます。2026-10-12 に、公式 Python SDK（`openai` 2.48.0）で動作を確認しています。

| モデル                 | 概要                                                                                       |
| ------------------- | ---------------------------------------------------------------------------------------- |
| `beat-moderation-1` | BeatAPI のコンテンツ判定モデルです。入力 1M トークンあたり \$0.042 です。リクエストの `model` は任意で、すべての呼び出しをこのモデルが処理します。 |

> **Note**
>
> 呼び出しが失敗した場合、判定は行われていません。タイムアウト、`4xx`、`503` などのエラーを「許可」とみなさないでください。コンテンツの公開・送信を保留するか、再試行してください。

## クイックスタート

```bash
curl https://api.beatapi.io/v1/moderations \
  -H "Authorization: Bearer $BEATAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"input": "Forged ID cards and bank cards for sale, shipped within 24 hours. Message me for the price list."}'
```

以下は、偽造身分証明書や銀行カードを販売する同種の広告に対して、実際に返されたレスポンスです。

```json
{
  "id": "modr-task_UnzjwhQHAITmqaLLJUi3ML5gQYnRfDln",
  "model": "beat-moderation-1",
  "results": [
    {
      "flagged": true,
      "categories": {
        "sexual": false,
        "sexual/minors": false,
        "violence": false,
        "hate": false,
        "harassment": false,
        "self-harm": false,
        "illicit": true,
        "harassment/threatening": false,
        "hate/threatening": false,
        "illicit/violent": false,
        "self-harm/intent": false,
        "self-harm/instructions": false,
        "violence/graphic": false
      },
      "category_scores": {
        "sexual": 0,
        "sexual/minors": 0.01,
        "violence": 0.02,
        "hate": 0.01,
        "harassment": 0.02,
        "self-harm": 0.01,
        "illicit": 0.97,
        "harassment/threatening": 0,
        "hate/threatening": 0,
        "illicit/violent": 0,
        "self-harm/intent": 0,
        "self-harm/instructions": 0,
        "violence/graphic": 0
      },
      "category_applied_input_types": {
        "sexual": ["text"],
        "sexual/minors": ["text"],
        "violence": ["text"],
        "hate": ["text"],
        "harassment": ["text"],
        "self-harm": ["text"],
        "illicit": ["text"],
        "harassment/threatening": [],
        "hate/threatening": [],
        "illicit/violent": [],
        "self-harm/intent": [],
        "self-harm/instructions": [],
        "violence/graphic": []
      },
      "verdict": "block",
      "sexual_level": "none"
    }
  ],
  "policy_version": "2026-10-12",
  "usage": { "input_tokens": 1206 }
}
```

`illicit` のスコアは `0.97` で、しきい値の `0.6` を超えています。そのため、`categories.illicit` と `flagged` は `true`、`verdict` は `block` になります。

### OpenAI SDK（Python）

クライアントの接続先を BeatAPI に設定し、通常どおり `moderations.create` を呼び出します。BeatAPI が追加するフィールドも、型付きの結果から取得できます。

```python
import os

from openai import OpenAI

client = OpenAI(
    api_key=os.environ["BEATAPI_API_KEY"],
    base_url="https://api.beatapi.io/v1",
)

resp = client.moderations.create(
    input="Forged ID cards and bank cards for sale, shipped within 24 hours. Message me for the price list.",
)
result = resp.results[0]

# The values in the comments are the ones from the response above.
print(result.flagged)                  # True
print(result.verdict)                  # "block" (added by BeatAPI)
print(result.categories.illicit)       # True
print(result.category_scores.illicit)  # 0.97
print(result.sexual_level)             # "none" (added by BeatAPI)
print(resp.policy_version)             # "2026-10-12" (added by BeatAPI)
```

### JavaScript

```javascript
const text = "Forged ID cards and bank cards for sale, shipped within 24 hours. Message me for the price list.";

const response = await fetch("https://api.beatapi.io/v1/moderations", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.BEATAPI_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ input: text }),
});

if (!response.ok) {
  // A failed call judged nothing: do not let the text through.
  // On HTTP 429, wait the Retry-After seconds before retrying.
  throw new Error(`Moderation HTTP ${response.status}; Retry-After: ${response.headers.get("Retry-After")}`);
}
const moderation = await response.json();
const result = moderation.results?.[0];
if (!result || !["allow", "review", "block"].includes(result.verdict)) {
  throw new Error("Invalid moderation response; hold the text for review");
}

if (result.verdict === "block") {
  console.log("rejected", result.category_scores);
} else if (result.verdict === "review") {
  console.log("queued for a person", result.sexual_level, result.category_scores);
} else {
  console.log("allowed under policy", moderation.policy_version);
}
```

## リクエスト

| フィールド   | 型               |  必須 | 説明                                                                                                                                                                                                                       |
| ------- | --------------- | --: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `input` | string \| array |  はい | 判定対象のテキスト 1 件です。文字列、または要素が 1 個だけの配列を指定します。配列の要素には文字列か `{"type": "text", "text": "..."}` を使用できます。最大 8,000 文字です。                                                                                                           |
| `model` | string          | いいえ | `beat-moderation-1`、または OpenAI のコンテンツ判定モデル名（`omni-moderation-latest`、`omni-moderation-2024-09-26`、`text-moderation-latest`、`text-moderation-stable`）を指定できます。どの名前でも `beat-moderation-1` が処理します。それ以外の名前は HTTP 400 で拒否されます。 |

## レスポンス

| フィールド                                    | 説明                                                                        |
| ---------------------------------------- | ------------------------------------------------------------------------- |
| `id`                                     | この呼び出しの識別子です。サポートへの問い合わせ時に伝えてください。                                        |
| `model`                                  | リクエストでどの対応モデル名を指定しても、常に `beat-moderation-1` です。                           |
| `results`                                | 呼び出しごとにテキストを 1 件判定するため、要素が 1 個の配列です。                                      |
| `results[].flagged`                      | いずれかのカテゴリのスコアがしきい値に達すると `true` になります。                                     |
| `results[].categories`                   | 各カテゴリのスコアがしきい値に達したかどうかを示します。                                              |
| `results[].category_scores`              | 各カテゴリの `0`～`1` のスコアです。                                                    |
| `results[].category_applied_input_types` | 判定対象の 7 カテゴリでは `["text"]`、6 つのサブカテゴリでは `[]` です。                           |
| `results[].verdict`                      | `allow`、`review`、`block` のいずれかです。BeatAPI が追加するフィールドです。                    |
| `results[].sexual_level`                 | `none`、`contextual`、`suggestive`、`explicit` のいずれかです。BeatAPI が追加するフィールドです。 |
| `policy_version`                         | テキストの判定に使用したポリシーのバージョンです。異なるバージョンのスコアは比較できません。BeatAPI が追加するフィールドです。       |
| `usage.input_tokens`                     | この呼び出しで計上された入力トークン数です。BeatAPI が追加するフィールドです。                               |

レスポンスは同期的に返され、成功した HTTP レスポンスに判定結果が含まれます。ポーリングは不要です。

## カテゴリ

OpenAI と同じ名前の 7 カテゴリを判定し、それぞれ `0`～`1` のスコアを返します。

| カテゴリ            | 検出する内容                                                  | 意図的に検出対象から除外する内容                                                                |
| --------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------- |
| `sexual`        | 露骨な性的内容、その要求、性的なロールプレイ、性的サービスやポルノの提供・広告。                | 医療、性教育、報道など、性的興奮を目的としない言及。露骨ではなく性的な示唆にとどまる内容も flagged の対象ではなく、`review` と判定されます。 |
| `sexual/minors` | 18 歳未満の人物を性的に扱うあらゆる内容。架空の人物や隠語で表現した場合も対象です。             | 子どもの保護、通報、年齢に適した教育のための内容。                                                       |
| `violence`      | 脅迫、暴力の扇動・賛美、凄惨な描写。                                      | 中立的な報道、歴史、スポーツ、ゲーム、残虐な描写を含まないフィクション。                                            |
| `hate`          | 人種、民族、国籍・出身地域、宗教、ジェンダー、性的指向、障害など、保護対象となる属性を理由に人々へ向ける憎悪。 | 憎悪表現を批判するための引用、思想や政府への批判。                                                       |
| `harassment`    | 個人や集団に対する侮辱、威圧、いじめ、個人情報の暴露、集団での攻撃。                      | 節度ある意見の相違、仕事や考えに対する率直な批判。                                                       |
| `self-harm`     | 自傷行為の推奨・方法の指示、または書き手が現在の自傷の意思を表明する内容。                   | 予防、支援、回復のための内容。                                                                 |
| `illicit`       | 犯罪を実行するための具体的な支援、違法な商品・サービスの提供や要求。                      | 報道、法律、被害の軽減、セキュリティ上の防御に関する内容。                                                   |

OpenAI の 6 つのサブカテゴリキー（`harassment/threatening`、`hate/threatening`、`illicit/violent`、`self-harm/intent`、`self-harm/instructions`、`violence/graphic`）は、型付き SDK との互換性を保つために含まれていますが、個別には判定されません。常に `false`、スコアは `0`、`category_applied_input_types` は空のリストです。これらを基に判定ルールを作らないでください。

## しきい値と総合判定

判定対象の各カテゴリにはしきい値があり、通常は `0.6`、`sexual/minors` は `0.3` です。`categories[k]` はカテゴリのスコアとしきい値の比較結果を示し、いずれかのスコアがしきい値に達すると `flagged` が `true` になります。

| `verdict` | 条件                                                                                                                                                        | 対処方法                         |
| --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------- |
| `block`   | 少なくとも 1 カテゴリのスコアがしきい値に達しています。`flagged: true` と同じ条件です。                                                                                                     | テキストを拒否します。                  |
| `review`  | どのスコアもしきい値に達していないものの、いずれかのスコアとしきい値との差が `0.25` 以内で、かつそのスコアが `0.15` 以上です。大半のカテゴリでは `0.35` 以上、`sexual/minors` では `0.15` 以上が対象です。または、露骨ではないものの性的な示唆を含むテキストです。 | 人による確認、またはより厳格なルールでの判定に回します。 |
| `allow`   | 上記のいずれにも該当しません。                                                                                                                                           | テキストを許可します。                  |

`sexual_level` は、スコアとは独立して、テキストの性的な内容の程度を示します。

| `sexual_level` | 意味                               |
| -------------- | -------------------------------- |
| `none`         | 性的な内容はありません。                     |
| `contextual`   | 医療、教育、報道など、性的興奮を目的とせずに性へ言及しています。 |
| `suggestive`   | 性的な示唆を含みますが、露骨な性的内容ではありません。      |
| `explicit`     | 露骨な性的内容です。                       |

`category_scores.sexual` は `explicit` に該当する確率です。性的な示唆にとどまるテキストでは、このスコアは低く、代わりに `sexual_level: "suggestive"` と `verdict: "review"` が返ります。そのような内容も判断の対象にする場合は、この 2 つのフィールドを確認してください。

より厳しい、または緩いしきい値が必要な場合は、`category_scores` に独自のしきい値を適用してください。

`policy_version` は、スコアを算出したポリシーを識別します。異なるポリシーバージョンのスコアは比較できないため、スコアを保存する際はバージョンも一緒に保存してください。

## 制限

* **テキストのみ。** `image_url` の入力は HTTP 400 で拒否されます。
* **1 回の呼び出しにつきテキスト 1 件。** 要素が複数ある配列は HTTP 400 で拒否され、一部だけが判定されることもありません。テキストごとにリクエストを送信してください。
* **最大 8,000 文字。** 超過したテキストは切り詰められず、HTTP 400 で拒否されます。分割して各部分を送信してください。長さは UTF-16 コードユニットで数えるため、多くの絵文字は 2 文字分として数えられます。
* **同期処理。** ストリーミングはなく、ポーリングするタスクもありません。サーバー側の処理時間は約 0.2 秒です。

## エラー

エラーは BeatAPI 標準の形式で返され、`code`、`message`、`retryable`、`request_id`、HTTP 429 の場合は `retry_after_seconds` を含みます。一覧と再試行の方針は[エラー](/errors)を参照してください。このエンドポイントのエラーオブジェクトには、OpenAI の `type` と `param`（常に `null`）も含まれるため、OpenAI SDK は通常どおり対応する例外を送出します。

```json
{
  "error": {
    "code": "bad_request",
    "message": "input is required: the text to moderate",
    "retryable": false,
    "request_id": "…",
    "type": "invalid_request_error",
    "param": null
  }
}
```

| HTTP ステータス | コード                                   | `type`                  | Python SDK の例外          | 意味                                                                                  |
| ---------: | ------------------------------------- | ----------------------- | ----------------------- | ----------------------------------------------------------------------------------- |
|        400 | `bad_request`                         | `invalid_request_error` | `BadRequestError`       | 空の入力、複数の入力、画像、8,000 文字の超過、未対応のモデル名、不正な JSON など、違反した条件がメッセージに示されます。                  |
|        401 | `missing_api_key` / `invalid_api_key` | `invalid_request_error` | `AuthenticationError`   | キーが送信されていないか、無効なキーです。                                                               |
|        402 | `insufficient_credits`                | `insufficient_quota`    | `APIStatusError`        | 残高不足です。OpenAI 自体は残高がない場合に HTTP 429 を返しますが、この API では HTTP 402 を返すため、SDK は自動で再試行しません。 |
|        403 | `forbidden`                           | `invalid_request_error` | `PermissionDeniedError` | キーまたはアカウントにこのモデルの利用権限がありません。一度もチャージしていないアカウントでは利用できません。                             |
|        429 | `rate_limit_exceeded`                 | `rate_limit_error`      | `RateLimitError`        | リクエストの上限に達しています。`Retry-After` の秒数だけ待ってください。同じ値が `retry_after_seconds` にも含まれます。      |
|        503 | `processing_unavailable`              | `server_error`          | `InternalServerError`   | コンテンツ判定サービスが応答できませんでした。待機時間を段階的に延ばして再試行してください。                                      |

表の例外は、2026-10-12 に公式 Python SDK（`openai` 2.48.0）が実際に送出したものです。ただし、HTTP 503 の行は SDK のステータス対応表に基づき、実際には発生させていません。

呼び出しが失敗した場合、判定は行われていません。エラーを「許可」とみなさないでください。HTTP 400 で拒否されたリクエストは課金されません。

## 課金

コンテンツ判定は**入力トークンのみ**を対象に、**入力 1M トークンあたり \$0.042** で課金されます。`beat-moderation-1` は[料金ページ](https://beatapi.io/pricing)に掲載されています。

判定ポリシーは毎回の呼び出しに含まれ、約 1,150 トークンが入力として計上されます。そのため、短いテキストでも最低限の入力使用量が発生します。

| テキスト         | 入力トークン数 |          料金 |
| ------------ | ------: | ----------: |
| 1 行          | 約 1,200 | ≈ \$0.00005 |
| 中国語 8,000 文字 | 約 9,200 |  ≈ \$0.0004 |

各レスポンスの `usage.input_tokens` で、実際に計上されたトークン数を確認し、呼び出しごとの費用を照合できます。

## プライバシー

送信したテキストはリクエストログに記録されません。保存されるのは、テキストの長さ、スコア、ポリシーバージョンのみです。

## 対応範囲と限界

* **分類器であり、結果を保証するものではありません。** 明白な違反は高い信頼性で検出します。一方、隠語、同音異義語、偽装したスパム、特に中国語でのこうした表現は、検出の信頼性が低くなります。利用の前に、自分のコンテンツで検証してください。
* **`sexual/minors` は、既知の虐待コンテンツを検出するハッシュ照合の代わりにはなりません。** また、法令上求められる通報義務を代替するものでもありません。
* **画像、音声、動画は判定しません。**
* **同じ入力でもスコアが完全に一致するとは限りません。** 当社のテストでは、同一の呼び出し間で最大 0.04 の変動がありました。しきい値に近いテキストは、そのどちら側にも判定される可能性があります。しきい値付近のスコアは境界的な結果として扱ってください。

## エージェント

エージェントは [Capabilities API](/capabilities) から同じ判定機能を利用できます。カタログ上の識別子は `model:beat-moderation-1` です。Run の入力は `{"input": "…"}` で、結果は上記と同じレスポンスボディです。この経路のエラーは、`type` と `param` を含まない標準形式で返されます。