Skip to main content

Short Answer

When a GPT model receives a request that violates the provider’s usage policy, it does not return an error. The API responds with HTTP 200, finish_reason is stop, and the content is a refusal written by the model itself, such as “I can’t help with that…”. The response has no error code and no refusal category or severity. It has exactly the same structure as a normal answer and is billed like one. Looking at the status code and finish_reason alone, you cannot tell that the request was refused.

The Refusal Response Body

Below is a real non-streaming refusal from /v1/chat/completions (the content has been replaced with a neutral example):

Why There Is No Category

For policy-violating requests, the OpenAI chat API lets the model decide not to answer instead of returning an error. It does not tell you which category was hit (for example adult content, graphic violence, or self-harm), and it gives no severity. This is different from an error. With an error you get a non-200 status and an error object. With a refusal you get a successful call whose content simply isn’t what you asked for.

Translation and Structured Output

Batch translation and extraction tasks usually ask the model for a fixed format, such as a JSON array. When a batch triggers a refusal, the model returns a plain sentence, so parsing it as JSON fails with errors like Unrecognized token 'I' or Expecting value. This is not an API format problem. The content of that batch was declined. Retrying the same batch unchanged usually gives the same result.
APIYI has enabled automatic failover for content safety on non-streaming requests. When one official route triggers a content filter, either on the prompt or on the generated output, the request is automatically retried on another official route, with no retry needed on your side. After failover most requests return a normal result; a small number may still be refused by the model itself, which is the case this page describes.

How to Detect and Handle It

1

Validate the output format first

If you asked for JSON, parse it as JSON; if you asked for a fixed number of items, check the count. If the format doesn’t match, treat the call as “no result” and don’t use the content as your output.
2

Then check whether it is a refusal

If the format doesn’t match, check whether the content is a short sentence starting with I can't, Sorry, or similar. If so, it is almost certainly a refusal rather than the model drifting off format.
3

Don't retry unchanged

Retrying the same content unchanged will most likely produce the same refusal, and each attempt is billed.
4

Split the batch to find the specific items

Resubmit the failed batch in smaller batches to find which items trigger the refusal; the rest usually complete normally. For the items that trigger it, adjust the wording and try again.
5

Archive failed cases, then decide whether to switch models

Keep an internal archive of failed cases (input, request time, request ID, returned content), review which kinds of content the refusals concentrate on, and then consider retrying that content with another model.
We recommend making “archive failed cases → analyze → retry with another model” a standard process. Refusals tend to cluster around a few kinds of content, so an archive makes the pattern easy to see. It saves repeated manual investigation and avoids paying for the same content again and again.
Here is a minimal example: validate the JSON, and record anything that doesn’t match in a local failure log.

Streaming Requests

Once a streaming response starts, it can no longer be switched to another route, so automatic content safety failover only applies to non-streaming requests. With streaming you may see:
  • a short refusal whose last event has finish_reason set to content_filter; or
  • part of the content already delivered, ending with finish_reason: "content_filter".
For tasks like batch translation that don’t need to be displayed token by token, we recommend non-streaming calls.

FAQ

Yes. A refusal is a successful call and is billed on actual input and output tokens, so avoid retrying the same content repeatedly.
No. Refusals are decided by the provider’s model under its usage policy. APIYI cannot turn them off or adjust how strict they are.
The model’s judgment has some randomness, and different official routes filter slightly differently, so borderline content may pass one time and be refused the next. Content that clearly violates the usage policy is refused consistently.
The OpenAI chat API does not return one. If your workflow needs categories, classify the content yourself before sending it, or sort the archived failed cases manually.

Response Handling

One parsing approach for streaming and non-streaming responses

How is content safety and compliance ensured?

Platform content safety and compliance policy