跳到导航

内容审查 API

内容审查 API

提交一段文本,拿回一个可以直接照着处理的结论:七个类别各有一个 0 到 1 的分数、各自是否过线,再加一个总的判定——allow(放行)、review(复核)或 block(拦截)。它用在用户写的和模型写的文本周围:评论发布之前、提示词送进模型之前、回复展示给用户之前。

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

请求和响应沿用 OpenAI Moderation API 的格式,所以 OpenAI 官方 SDK 只需改 base URL 和 API Key 就能用。2026-10-12 已用官方 Python SDK(openai 2.48.0)实测。

模型说明
beat-moderation-1BeatAPI 的内容审查模型。输入 $0.042 / 百万 token。请求里的 model 可以不填,每次调用都由它作答。

调用失败就等于什么都没审。超时、4xx、503,任何错误都不能当成「放行」:先扣住内容,或者重试。

快速开始

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."}'

下面是一条真实响应,当时送审的就是这样一则兜售伪造身份证和银行卡的广告。

{
"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 为 true,flagged 为 true,verdict 为 block。

OpenAI SDK(Python)

把客户端指向 BeatAPI,照常调用 moderations.create。BeatAPI 多给的字段可以直接从类型化的结果上读。

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

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);
}

请求参数

字段类型必填说明
inputstring | array是要审查的那一段文本:一个字符串,或只含一项的数组,这一项可以是字符串或 {"type": "text", "text": "..."}。最长 8,000 个字符。
modelstring否beat-moderation-1,或 OpenAI 的审核模型名(omni-moderation-latest、omni-moderation-2024-09-26、text-moderation-latest、text-moderation-stable),这些名字都接受,并且都由 beat-moderation-1 作答。其他名字返回 400。

响应

字段说明
id本次调用的标识。提工单时附上它。
model固定是 beat-moderation-1,无论请求里用的是哪个被接受的名字。
results只有一项的数组,因为一次调用只审一段文本。
results[].flagged任何一个类别的分数达到它的拦截线时为 true。
results[].categories每个类别的分数是否达到它的拦截线。
results[].category_scores每个类别一个 0 到 1 的分数。
results[].category_applied_input_types七个参与判定的类别是 ["text"],六个子类别是 []。
results[].verdictallow、review 或 block。BeatAPI 多给的字段。
results[].sexual_levelnone、contextual、suggestive 或 explicit。BeatAPI 多给的字段。
policy_version作出这次判定的审查标准版本。不同版本的分数不可比。BeatAPI 多给的字段。
usage.input_tokens本次调用计入的输入 token 数。BeatAPI 多给的字段。

响应是同步的:HTTP 成功返回时,判定结果就在响应里,没有轮询。

类别

参与判定的有七个类别,名字和 OpenAI 的一致,各有一个 0 到 1 的分数。

类别会命中有意不命中
sexual露骨的性内容、索要这类内容的请求、色情角色扮演,以及性服务或色情内容的招揽和广告。医学、性教育、新闻等不以挑起性欲为目的的提及。仅仅是擦边的内容也不命中,它得到的是 review 判定。
sexual/minors任何对未满 18 岁者的性化,无论包装成虚构还是用了暗语。儿童保护、举报,以及适龄的教育内容。
violence威胁、煽动、美化暴力,以及血腥细节。中性的新闻、历史、体育、游戏,或不含血腥描写的虚构作品。
hate因受保护特征而对人群的仇恨:种族、民族、国籍或地域、宗教、性别、性取向、残障等。为了批评而引用仇恨言论,以及对观点和政府的批评。
harassment针对个人或群体的辱骂、恐吓、霸凌、人肉搜索和围攻。有礼貌的不同意见,以及对作品或观点的直率批评。
self-harm鼓吹或教唆自我伤害,或者作者本人表达了当下的自伤意图。预防、支持和康复类内容。
illicit可以照着去实施犯罪的帮助,以及违法物品和服务的兜售或求购。新闻、法律、减害和安全防御类内容。

OpenAI 的六个子类别键——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至少一个类别的分数达到了它的拦截线。等同于 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 是「露骨」这一档的概率。擦边的文本在这里得分很低,它体现在 sexual_level: "suggestive" 和 verdict: "review" 上;在意擦边内容的话,请看这两个字段。

想要比这更严或更松的线,就对 category_scores 套用你自己的阈值。

policy_version 标识给出这些分数的审查标准。不同标准版本的分数不可比,所以存分数时请把版本一起存下。

限制

  • 只审文本。 带 image_url 的输入返回 400。
  • 一次只审一段文本。 数组里超过一项返回 400——不会只审其中一部分。每段文本各发一次请求。
  • 最长 8,000 个字符。 超长返回 400,不会截断后只审前半段;请拆开后逐段发送。长度按 UTF-16 码元计,所以多数 emoji 算两个字符。
  • 同步。 没有流式,也没有需要轮询的任务。服务端处理约 0.2 秒。

错误

错误使用 BeatAPI 标准错误结构:code、message、retryable、request_id,429 时还有 retry_after_seconds。完整错误码表与重试策略见错误码。在这个接口上,错误对象还会带上 OpenAI 的 type 和 param(param 固定为 null),所以 OpenAI SDK 会照常抛出对应的异常。

{
"error": {
"code": "bad_request",
"message": "input is required: the text to moderate",
"retryable": false,
"request_id": "…",
"type": "invalid_request_error",
"param": null
}
}
HTTP 状态码codetypePython SDK 抛出含义
400bad_requestinvalid_request_errorBadRequestError错误信息会说明违反了哪条规则:输入为空、多段输入、图片、超过 8,000 个字符、不支持的模型名,或 JSON 不合法。
401missing_api_key / invalid_api_keyinvalid_request_errorAuthenticationError没有带密钥,或密钥无效。
402insufficient_creditsinsufficient_quotaAPIStatusError余额不足。OpenAI 自己在余额为空时返回 429;这里是 402,SDK 不会自动重试它。
403forbiddeninvalid_request_errorPermissionDeniedError该密钥或账户不能使用这个模型。从未充值过的账户不能使用它。
429rate_limit_exceededrate_limit_errorRateLimitError触发速率限制。按 Retry-After 响应头给出的秒数等待,同样的数值也在 retry_after_seconds 里。
503processing_unavailableserver_errorInternalServerError审查服务未能作答。退避重试。

表中的异常类型是 2026-10-12 用官方 Python SDK(openai 2.48.0)实际抛出的;503 那一行没有实际触发过,是按 SDK 的状态码映射写的。

调用失败就等于什么都没审——绝不能把错误当成「放行」。被 400 拒绝的请求不计费。

计费

内容审查只按输入 token 计费,单价 $0.042 / 1M 输入 token。beat-moderation-1 列在价目页上。

审查标准随每次调用一起发送,并计入输入,约 1,150 token,所以再短的文本也有一个底数:

文本输入 token费用
一行字约 1,200≈ $0.00005
8,000 个汉字约 9,200≈ $0.0004

每次响应里的 usage.input_tokens 就是这次实际计入的量,可以逐次对账。

隐私

你提交的文本不会写入请求日志。留存的只有它的长度、各类分数和审查标准版本。

它做不到的事

  • 它是分类器,不是保证。 直白的违规内容判得可靠;黑话、谐音和伪装过的引流广告(尤其是中文)判得没那么可靠。依赖它之前,请先用你自己的内容测一遍。
  • sexual/minors 不能代替哈希比对(针对已知的儿童性侵害材料),也不免除你的任何法定上报义务。
  • 它不审图片、音频和视频。
  • 分数不是完全可复现的。 我们实测,完全相同的调用之间分数最多相差 0.04,所以贴近拦截线的文本可能时而过线、时而不过。分数贴线的内容请当作临界情况处理。

供 Agent 调用

Agent 可以通过能力 API做同样的审查,它在能力目录里是 model:beat-moderation-1。Run 的入参是 {"input": "…"},结果就是上面这个响应体。走这条路径时,错误是标准错误结构,不带 type 和 param。