> 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.

# 콘텐츠 검수 API

> 텍스트 하나 또는 이미지 하나를 7개 콘텐츠 카테고리 기준으로 검사하고, 카테고리별 점수와 allow·review·block 판정을 OpenAI Moderation API와 같은 요청·응답 형식으로 받습니다.

# 콘텐츠 검수 API

텍스트 하나와 이미지 최대 4장을 보내면 바로 활용할 수 있는 판정을 받습니다. 7개 카테고리마다 `0`\~`1` 점수, 각 카테고리의 기준선 초과 여부, 그리고 `allow`, `review`, `block` 중 하나의 최종 판정이 반환됩니다. 사용자가 쓰거나 모델이 만든 콘텐츠를 검사하는 데 맞춰 설계되었습니다. 게시 전의 댓글, 모델에 전달되기 전의 프롬프트나 참조 이미지, 사용자에게 보이기 전의 답변이나 생성 이미지를 검사하세요.

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

요청과 응답이 OpenAI Moderation API를 따르므로, 공식 OpenAI SDK에서 기본 URL과 API 키만 바꾸면 됩니다. 2026-10-13에 공식 Python SDK(`openai` 3.29.0)로 텍스트와 이미지 모두 검증했습니다.

| 모델                           | 응답 모델                                        | 가격                |
| ---------------------------- | -------------------------------------------- | ----------------- |
| `beat-moderation-2.0`        | 기본값. 텍스트 하나와 이미지 최대 4장을 함께 판정하거나 텍스트만 판정합니다. | 입력 1M 토큰당 \$0.08  |
| `beat-moderation-1.0`        | 이름을 지정했을 때의 이전 모델: 텍스트만 있는 요청.               | 입력 1M 토큰당 \$0.065 |
| `beat-moderation-vision-1.0` | 이름을 지정했을 때의 이전 모델: 이미지가 포함된 요청(텍스트 유무 무관).   | 입력 1M 토큰당 \$0.23  |

**응답하는 모델.** `model`은 선택 사항입니다. `model`이 없거나 OpenAI의 검수 모델 이름을 보낸 요청에는 `beat-moderation-2.0`이 응답합니다. `beat-moderation-1.0`과 `beat-moderation-vision-1.0`은 이름을 지정했을 때만 응답하며, 이전 제한(텍스트 하나, 이미지 하나 또는 각각 하나씩)과 자체 기준선을 유지합니다. 2026-10-13 이전에 쓰던 이름 `beat-moderation-1`과 `beat-moderation-vision-1`도 여전히 이전 모델로 연결됩니다. 어느 모델이 응답했는지는 응답의 `model`에 표시됩니다.

> **Note**
>
> 실패한 호출은 아무것도 판정하지 않은 것입니다. 시간 초과, `4xx`, `503` 같은 오류를 "허용"으로 처리하지 마세요. 콘텐츠를 보류하거나 재시도하세요.

## Beat Moderation 2.0

`beat-moderation-2.0`이 기본 모델입니다. 텍스트 하나와 이미지 최대 4장을 한 번의 호출로 함께 판정합니다. 어느 하나에라도 해당하면 그 카테고리가 적용되므로, 무해한 이미지가 유해한 캡션을 가리지 못하고 무해한 캡션도 유해한 이미지를 가리지 못합니다. 엔드포인트, 요청 형식, 응답 형식은 이전 모델과 같습니다. 텍스트만 보내거나 텍스트와 이미지를 함께 보내세요.

```bash
curl https://api.beatapi.io/v1/moderations \
  -H "Authorization: Bearer $BEATAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "beat-moderation-2.0",
    "input": [
      {"type": "text", "text": "Product photos for my shop listing."},
      {"type": "image_url", "image_url": {"url": "https://example.com/photo-1.jpg"}},
      {"type": "image_url", "image_url": {"url": "https://example.com/photo-2.jpg"}}
    ]
  }'
```

```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(
    model="beat-moderation-2.0",
    input=[
        {"type": "text", "text": "Product photos for my shop listing."},
        {"type": "image_url", "image_url": {"url": "https://example.com/photo-1.jpg"}},
        {"type": "image_url", "image_url": {"url": "https://example.com/photo-2.jpg"}},
    ],
)
print(resp.model)               # "beat-moderation-2.0"
print(resp.results[0].verdict)  # "allow", "review" or "block"
```

### 입력

* **텍스트 하나와 이미지 최대 4장.** 텍스트 파트는 최대 하나(8,000자 이하), `image_url` 파트는 최대 4개입니다.
* **링크 또는 인라인.** 이미지는 BeatAPI가 가져오는 공개 `http(s)` 링크(최대 2,048자) 또는 base64 데이터 URL입니다.
* **PNG, JPEG, WebP 또는 GIF(첫 프레임), 각각 최대 16 MB, 픽셀 크기 제한 없음.** BeatAPI가 모델에 맞게 각 이미지 크기를 조정하고 휴대폰 사진의 방향을 바로잡습니다.
* 2.0의 응답에는 `"model": "beat-moderation-2.0"`과 `"policy_version": "2026-10-15"`가 있으며, 이미지를 보냈다면 `category_applied_input_types`는 `["text", "image"]`입니다. 나머지는 아래에 설명한 응답과 같습니다.

### 400으로 거부되는 경우

| 요청                                  | 이유                          |
| ----------------------------------- | --------------------------- |
| 이미지 4장 초과                           | 호출당 이미지는 최대 4장입니다.          |
| 100메가픽셀을 넘는 이미지                     | 먼저 크기를 줄이세요.                |
| 화면비가 1:5.6보다 좁은 이미지                 | 짧은 변은 긴 변의 1/5.6 이상이어야 합니다. |
| base64가 아닌 데이터 URL                  | base64 데이터 URL만 읽습니다.       |
| 읽을 수 있는 PNG, JPEG, WebP, GIF가 아닌 파일 | HEIC와 AVIF는 받지 않습니다.        |

일부만 판정하는 경우는 없으며, 400으로 거부된 요청은 과금되지 않습니다.

### 기준선과 판정

카테고리는 `0.6`에서 플래그되며(판정은 `block`), 예외는 다음과 같습니다. `verdict`는 카테고리 기준선보다 0.25 낮은 점수부터 `review`이며, `0.15`보다 낮아지지 않습니다.

| 요청     | 카테고리            |    기준선 | 검토 시작점 |
| ------ | --------------- | -----: | -----: |
| 텍스트만   | `sexual/minors` |  `0.3` | `0.15` |
| 텍스트만   | `harassment`    |  `0.8` | `0.55` |
| 텍스트만   | 그 밖의 모든 카테고리    |  `0.6` | `0.35` |
| 이미지 포함 | `sexual/minors` | `0.35` | `0.15` |
| 이미지 포함 | `self-harm`     |  `0.5` | `0.25` |
| 이미지 포함 | 그 밖의 모든 카테고리    |  `0.6` | `0.35` |

더 엄격하거나 느슨한 기준이 필요하면 `category_scores`에 자체 기준선을 적용하세요.

### 가격과 토큰 규모

**입력 1M 토큰당 \$0.08**, 출력은 무료입니다. 측정된 규모:

| 요청                    |                  입력 토큰 |
| --------------------- | ---------------------: |
| 짧은 텍스트                |                약 1,200 |
| 이미지 한 장(짧은 텍스트 유무 무관) | 평균 약 3,800, 최대 약 5,100 |
| 이미지 한 장 추가마다          |          최대 약 4,800 추가 |

모든 응답의 `usage.input_tokens`에 호출에서 센 토큰 수가 정확히 표시됩니다. 텍스트와 이미지는 로그에 남지 않습니다.

## 빠른 시작

```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_untsppzSTLTDbt7Kw360kbf4ydPBYOAS",
  "model": "beat-moderation-2.0",
  "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.99,
        "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-15",
  "usage": { "input_tokens": 1210 }
}
```

`illicit`이 `0.99`점으로 기준선 `0.6`을 넘었으므로 `categories.illicit`은 `true`, `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.99
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 content 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 content 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);
}
```

## 이미지

이미지는 OpenAI API와 같은 방식으로 `input` 배열에 `image_url` 파트로 넣습니다. 이미지만 보내거나, 관련 텍스트(프롬프트, 캡션)를 같은 배열에 `text` 파트로 함께 보내세요.

```bash
curl https://api.beatapi.io/v1/moderations \
  -H "Authorization: Bearer $BEATAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "input": [
      {"type": "text", "text": "Remove her clothes and make it a nude photo."},
      {"type": "image_url", "image_url": {"url": "https://beatapi.io/samples/ai-ecommerce-video-demo-poster.jpg"}}
    ]
  }'
```

이 요청의 이미지는 평범한 제품 포스터입니다. 이미지만 보내면 허용되지만, 이 프롬프트와 함께 보냈을 때 실제 응답은 `verdict: "block"`였고 `sexual`은 `1`, `sexual_level: "explicit"`였습니다. 이미지와 텍스트를 함께 판정하고 텍스트도 따로 채점하므로, 무해한 이미지가 그 이미지에 대한 유해한 요청을 가리지 못합니다.

```python
resp = client.moderations.create(
    input=[
        {"type": "text", "text": "Change the background to a beach."},
        {"type": "image_url", "image_url": {"url": "https://example.com/upload.jpg"}},
    ],
)
print(resp.model)                # "beat-moderation-vision-1.0"
print(resp.results[0].verdict)   # "allow", "review" or "block"
```

* **링크 또는 인라인.** `image_url.url`은 `http(s)` 링크 또는 `data:image/jpeg;base64,...` 같은 base64 데이터 URL입니다. 링크는 BeatAPI가 가져오므로 공개적으로 접근할 수 있어야 하며, 사설 주소로 연결되는 링크는 거부됩니다.
* **PNG, JPEG 또는 WebP, 최대 4 MB**, 16메가픽셀 이하. 형식은 데이터 URL의 표기가 아니라 파일 자체로 판별하며, 줄바꿈이 있거나 URL 안전 문자 집합을 쓰거나 패딩이 없는 base64도 받습니다.
* **호출당 이미지 한 장**, 텍스트는 최대 하나(8,000자 이하).
* 이미지에 대한 응답에는 `"model": "beat-moderation-vision-1.0"`, `"policy_version": "2026-10-13"`, 그리고 `["image"]`(텍스트를 함께 보냈다면 `["text", "image"]`)인 `category_applied_input_types`가 있습니다. 나머지는 아래에 설명한 응답과 같습니다.
* 호출 한 번에 약 0.5초가 걸리며, 링크를 가져오는 시간이 더해집니다.

## 요청

| 필드      | 타입              |  필수 | 설명                                                                                                                                                                                                                                                                                                                                                                             |
| ------- | --------------- | --: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `input` | string \| array |   예 | 검사할 대상입니다. 문자열은 텍스트 하나입니다. 배열에는 텍스트 하나(문자열 또는 `{"type": "text", "text": "..."}`)와 이미지 최대 4장(`{"type": "image_url", "image_url": {"url": "..."}}`)을 넣습니다. 텍스트는 최대 8,000자입니다. 이름을 지정한 이전 모델은 텍스트 하나, 이미지 하나 또는 각각 하나씩을 받습니다.                                                                                                                                                     |
| `model` | string          | 아니요 | `beat-moderation-2.0`, 이전 모델을 계속 쓰려면 `beat-moderation-1.0` 또는 `beat-moderation-vision-1.0`, 또는 OpenAI 검수 모델 이름(`omni-moderation-latest`, `omni-moderation-2024-09-26`, `text-moderation-latest`, `text-moderation-stable`) 중 하나입니다. 각 모델의 동작은 이 페이지 위쪽의 *응답하는 모델*을 참고하세요. 2026-10-13 이전에 쓰던 이름 `beat-moderation-1`과 `beat-moderation-vision-1`도 계속 받습니다. 그 밖의 이름은 400으로 거부됩니다. |

## 응답

| 필드                                       | 설명                                                                                                                                                        |
| ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                                     | 이 호출의 식별자입니다. 지원 티켓에 함께 적어 주세요.                                                                                                                           |
| `model`                                  | 응답한 모델: `beat-moderation-2.0`, `beat-moderation-1.0` 또는 `beat-moderation-vision-1.0`.                                                                     |
| `results`                                | 항목이 하나인 배열입니다. 호출 한 번에 텍스트와 이미지를 함께 판정합니다.                                                                                                                |
| `results[].flagged`                      | 어느 카테고리든 점수가 기준선에 도달하면 `true`입니다.                                                                                                                         |
| `results[].categories`                   | 카테고리별로 점수가 기준선에 도달했는지 여부입니다.                                                                                                                              |
| `results[].category_scores`              | 카테고리별 `0`\~`1` 점수입니다.                                                                                                                                     |
| `results[].category_applied_input_types` | 판정한 7개 카테고리가 적용된 대상: `["text"]`, `["image"]` 또는 `["text", "image"]`. 6개 하위 카테고리는 `[]`입니다.                                                                 |
| `results[].verdict`                      | `allow`, `review` 또는 `block`. BeatAPI가 추가한 필드입니다.                                                                                                         |
| `results[].sexual_level`                 | `none`, `contextual`, `suggestive` 또는 `explicit`. BeatAPI가 추가한 필드입니다.                                                                                     |
| `policy_version`                         | 콘텐츠를 판정한 정책 버전입니다. `beat-moderation-2.0`은 `2026-10-15`이며, 이전 모델은 텍스트에 `2026-10-12`, 이미지에 `2026-10-13`입니다. 정책 버전이 다른 점수끼리는 비교할 수 없습니다. BeatAPI가 추가한 필드입니다. |
| `usage.input_tokens`                     | 이 호출에서 센 입력 토큰 수입니다. BeatAPI가 추가한 필드입니다.                                                                                                                  |

응답은 동기식입니다. HTTP 응답이 성공하면 판정이 담겨 있으며, 폴링할 필요가 없습니다.

## 카테고리

7개 카테고리를 OpenAI와 같은 이름으로 판정하며, 각각 `0`\~`1` 점수를 매깁니다. 텍스트와 이미지에 같은 7개 카테고리가 적용됩니다.

| 카테고리            | 플래그 대상                                                                     | 의도적으로 플래그하지 않는 대상                                                                              |
| --------------- | -------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| `sexual`        | 노골적인 성적 콘텐츠, 그런 콘텐츠 요청, 성적 역할극, 성매매나 음란물 제공·광고. 이미지에서는 음란물이나 성적 흥분을 노린 노출. | 의료, 성교육, 뉴스 등 성적 흥분과 무관한 언급, 고전 미술, 의료 이미지, 일반 수영복. 단순히 암시적인 콘텐츠도 플래그하지 않으며 `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` 목록은 비어 있습니다. 이 키로 규칙을 만들지 마세요.

## 기준선과 판정

판정하는 모든 카테고리에는 기준선이 있습니다. `categories[k]`는 해당 카테고리 점수를 기준선과 비교한 결과이며, 어느 카테고리든 점수가 기준선에 도달하면 `flagged`는 `true`입니다.

|                                   | 모든 카테고리의 기준선 | `sexual/minors`의 기준선 | 검토 시작점                          |
| --------------------------------- | -----------: | -------------------: | ------------------------------- |
| 텍스트(`beat-moderation-1.0`)        |        `0.6` |                `0.3` | `0.35`, `sexual/minors`는 `0.15` |
| 이미지(`beat-moderation-vision-1.0`) |        `0.6` |               `0.35` | `0.35`, `sexual/minors`는 `0.25` |

Beat Moderation 2.0은 자체 기준선이 있으며, 이 페이지 위쪽의 해당 섹션에 나와 있습니다. 위 표는 이름을 지정해 이전 모델을 쓸 때의 기준입니다.

| `verdict` | 발생 조건                                                                 | 조치                         |
| --------- | --------------------------------------------------------------------- | -------------------------- |
| `block`   | 하나 이상의 카테고리 점수가 기준선에 도달했습니다. `flagged: true`와 같습니다.                   | 콘텐츠를 거부하세요.                |
| `review`  | 기준선에 도달한 점수는 없지만 그 아래의 검토 구간에 든 점수가 있거나, 노골적이지는 않지만 성적으로 암시적인 콘텐츠입니다. | 사람이 검토하거나 더 엄격한 규칙을 적용하세요. |
| `allow`   | 위 두 경우에 해당하지 않습니다.                                                    | 통과시키세요.                    |

`sexual_level`은 점수와 별개로 콘텐츠가 얼마나 성적인지 나타냅니다.

| `sexual_level` | 의미                                          |
| -------------- | ------------------------------------------- |
| `none`         | 성적 콘텐츠가 없습니다.                               |
| `contextual`   | 성적 흥분 의도가 없는 성적 내용이나 노출: 의료, 교육, 뉴스, 고전 미술. |
| `suggestive`   | 노골적이지는 않지만 성적으로 암시적입니다.                     |
| `explicit`     | 노골적인 성적 콘텐츠입니다.                             |

`category_scores.sexual`은 `explicit` 수준일 확률입니다. 암시적인 콘텐츠는 이 값이 낮게 나오고 대신 `sexual_level: "suggestive"`와 `verdict: "review"`로 나타나므로, 암시적인 콘텐츠가 중요하다면 이 두 필드를 확인하세요. 이미지와 텍스트를 함께 보내면 둘 중 높은 값이 표시됩니다.

이보다 엄격하거나 느슨한 기준이 필요하면 `category_scores`에 자체 기준선을 적용하세요.

`policy_version`은 점수를 산출한 정책을 나타냅니다. 정책 버전이 다른 점수끼리, 그리고 텍스트 모델과 이미지 모델의 점수끼리는 비교할 수 없으므로 점수를 저장할 때는 버전도 함께 저장하세요.

## 한도

이 제한은 이름을 지정해 이전 모델을 쓸 때 적용됩니다. Beat Moderation 2.0은 텍스트 하나와 이미지 최대 4장을 받으며, 제한은 이 페이지 위쪽의 해당 섹션에 있습니다.

* **호출당 텍스트 하나, 이미지 하나 또는 각각 하나씩.** 텍스트나 이미지가 둘 이상이면 400으로 거부되며, 일부만 판정하지 않습니다. 항목마다 요청을 하나씩 보내세요.
* **텍스트는 최대 8,000자입니다.** 더 긴 텍스트는 잘리지 않고 400으로 거부되므로 나눠서 보내세요. 길이는 UTF-16 코드 단위로 세므로 대부분의 이모지는 2자로 계산됩니다.
* **이미지는 PNG, JPEG 또는 WebP이며 최대 4 MB, 16메가픽셀입니다.** 그 밖의 경우는 무엇이 잘못되었는지 알려 주는 400으로 거부됩니다.
* **오디오와 영상은 지원하지 않습니다.**
* **동기식입니다.** 스트리밍도 폴링할 작업도 없습니다. 서버 측 처리 시간은 텍스트가 약 0.2초, 이미지가 약 0.5초입니다.

## 오류

오류는 표준 BeatAPI 오류 형식(`code`, `message`, `retryable`, `request_id`, 그리고 429일 때 `retry_after_seconds`)을 사용합니다. 전체 표와 재시도 정책은 [오류](/errors)를 참고하세요. 이 엔드포인트의 오류 객체에는 OpenAI의 `type`과 `param`도 포함되므로(`param`은 항상 `null`) OpenAI SDK가 평소와 같은 예외를 발생시킵니다.

```json
{
  "error": {
    "code": "bad_request",
    "message": "input is required: the text to moderate, an image, or one of each",
    "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는 잔액이 없으면 429로 응답하지만 여기서는 402이며, SDK가 자동으로 재시도하지 않습니다.                               |
|     403 | `forbidden`                                    | `invalid_request_error` | `PermissionDeniedError` | 키나 계정이 이 모델을 사용할 수 없습니다. 한 번도 충전하지 않은 계정은 사용할 수 없습니다.                                                |
|     429 | `rate_limit_exceeded`                          | `rate_limit_error`      | `RateLimitError`        | 요청 한도에 도달했습니다. `Retry-After`초 기다리세요. 같은 값이 `retry_after_seconds`에도 있습니다.                             |
|     503 | `processing_unavailable` / `processing_failed` | `server_error`          | `InternalServerError`   | 검수 서비스가 응답하지 못했습니다. 백오프를 두고 재시도하세요.                                                                  |

이미지에 대한 400 메시지:

| 이미지                                                         | `message`                                                                                                                                   |
| ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| PNG, JPEG, WebP가 아님(GIF, BMP, HEIC 사진, SVG, 이미지가 아닌 파일)     | `the image data is a GIF, which is not supported; send PNG, JPEG or WebP` 또는 `The image could not be read. Send a PNG, JPEG or WebP image.` |
| 4 MB 초과                                                     | `image is about 5.2 MB; the limit is 4 MB. Resize or recompress it`, 링크라면 `An input file is larger than the 4 MB limit. …`                  |
| 16메가픽셀 초과                                                   | `The image has too many pixels; the limit is 16 megapixels. Resize it and try again.`                                                       |
| 가져올 수 없는 링크: 404, 연결을 거부하거나 응답하지 않는 호스트, 유효하지 않은 인증서, 사설 주소 | `An input file could not be downloaded. Upload it again or use a publicly reachable HTTPS URL, then retry.`                                 |
| 웹 페이지나 빈 파일을 반환하는 링크                                        | `An input link did not return an image. Link directly to the image file.` / `An input link returned an empty file.`                         |
| 링크도 base64 데이터 URL도 아니거나, 올바른 base64가 아님                    | `image_url.url must be an http(s) link to the image, or a base64 data URL such as data:image/jpeg;base64,...`                               |

손상되었거나 잘린 이미지는 이 검사를 통과한 뒤 모델에서 실패할 수 있습니다. 이 경우 파일이 손상되었을 수 있다는 메시지와 함께 503이 반환되며, 같은 파일로 재시도해도 다시 실패합니다.

첫 번째 표의 예외는 공식 Python SDK가 실제로 발생시킨 것입니다. 2026-10-12에 401, 402, 403, 429를(`openai` 2.48.0), 2026-10-13에 400과 503을 다시 확인했으며(`openai` 3.29.0), 503은 잘린 JPEG에 대한 것입니다.

실패한 호출은 아무것도 판정하지 않은 것이므로 오류를 "허용"으로 처리하지 마세요. 400으로 거부된 요청은 과금되지 않습니다.

## 과금

콘텐츠 검수는 **입력 토큰에만** 과금됩니다. 기본 모델 `beat-moderation-2.0`은 **입력 1M 토큰당 $0.08**입니다. 이름을 지정해 이전 모델을 쓰면 텍스트는 **입력 1M 토큰당 $0.065**, 이미지가 포함된 요청은 **입력 1M 토큰당 \$0.23**입니다. 세 모델 모두 [가격 페이지](https://beatapi.io/pricing)에 있습니다.

이전 모델에서는 검수 정책이 호출마다 함께 전달되어 약 1,150토큰의 입력으로 계산되므로, 짧은 텍스트에도 최소 비용이 있습니다. 이미지는 크기와 관계없이 최대 약 1,000토큰이 추가됩니다.

이전 모델의 토큰 규모(2.0의 값은 이 페이지 위쪽의 해당 섹션 참고):

| 요청                  |          입력 토큰 |          비용 |
| ------------------- | -------------: | ----------: |
| 텍스트 한 줄             |        약 1,200 | ≈ \$0.00007 |
| 한자 8,000자           |        약 9,200 |  ≈ \$0.0006 |
| 이미지 한 장             | 약 2,000\~2,500 |  ≈ \$0.0005 |
| 짧은 프롬프트가 있는 이미지 한 장 |        약 2,600 |  ≈ \$0.0006 |

모든 응답의 `usage.input_tokens`에 호출에서 센 토큰 수가 정확히 표시되므로 호출별로 지출을 대조할 수 있습니다.

## 개인정보 보호

제출한 텍스트와 이미지는 요청 로그에 기록되지 않습니다. 텍스트 길이, 이미지 수, 점수, 정책 버전만 보관합니다. 링크로 보낸 이미지는 해당 요청을 위해 한 번만 가져옵니다.

## 하지 않는 것

* **분류기일 뿐 보증이 아닙니다.** 명백한 위반은 안정적으로 잡아냅니다. 은어, 동음이의어, 위장한 스팸(특히 중국어)은 덜 안정적으로 잡습니다. 사용하기 전에 실제 콘텐츠로 테스트하세요.
* **경계선에 있는 이미지는 텍스트보다 판정이 덜 일관됩니다.** 예술적 누드, 격투 스포츠, 역사 사진은 기준선 근처 점수를 받으며, 비슷한 이미지라도 `allow`, `review` 또는 `block`으로 나올 수 있습니다. 이미지 안에 그려진 텍스트(밈, QR 코드 광고)는 테스트에 포함하지 않았습니다.
* **`sexual/minors`는 알려진 학대물에 대한 해시 매칭이나 법적 신고 의무를 대신하지 않습니다.**
* **오디오와 영상은 검사하지 않습니다.**
* **점수가 정확히 재현되지는 않습니다.** 테스트에서 같은 호출끼리 최대 0.04까지 차이가 났으므로, 기준선 근처 점수를 받은 콘텐츠는 어느 쪽으로든 판정될 수 있습니다. 기준선 근처 점수는 경계 사례로 다루세요.

## 에이전트

에이전트는 [Capabilities API](/capabilities)를 통해 같은 검사를 사용할 수 있으며, 카탈로그에 `model:beat-moderation-1.0`과 `model:beat-moderation-vision-1.0`으로 등록되어 있습니다. Run 입력은 텍스트는 `{"input": "…"}`, 이미지는 `{"image_url": "…", "input": "optional text"}`이며, 결과 본문은 위와 같습니다. 이 경로의 오류는 `type`과 `param`이 없는 표준 오류 형식을 사용합니다.

Beat Moderation 2.0은 `model:beat-moderation-2.0`입니다. Run 입력은 `{"input": "text", "image_urls": ["https://..."]}`이며, 이미지 한 장은 `image_url`로도 보낼 수 있습니다.