Develop / Guides
OpenAI SDK
How to point the OpenAI SDK for Python and TypeScript at the gateway, set Noirdoc headers and tell gateway errors apart from provider errors.
The gateway accepts requests in the format of the OpenAI API. The OpenAI SDK therefore works unchanged as soon as base_url and api_key point to Noirdoc. The examples use the model ID qwen3.8-27b. GET /v1/models returns the model IDs your key may call (see Quickstart).
Install the SDK
Install the openai package for Python or for Node.js.
pip install openainpm install openaiSet up the client
Set base_url to https://api.noirdoc.de/v1 and api_key to your px- key. The SDK sends the key in the Authorization: Bearer header.
Both SDKs also read the values from the environment variables OPENAI_BASE_URL and OPENAI_API_KEY. This lets you set up existing code without changing it.
The client covers all OpenAI endpoints of the gateway, for example client.responses.create for POST /v1/responses and client.embeddings.create for POST /v1/embeddings.
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.noirdoc.de/v1",
api_key=os.environ["NOIRDOC_API_KEY"],
)
response = client.chat.completions.create(
model="qwen3.8-27b",
messages=[{"role": "user", "content": "Hello"}],
)
print(response.choices[0].message.content)import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api.noirdoc.de/v1",
apiKey: process.env.NOIRDOC_API_KEY,
});
const response = await client.chat.completions.create({
model: "qwen3.8-27b",
messages: [{ role: "user", content: "Hello" }],
});
console.log(response.choices[0].message.content);Turn on Noirdoc Shield
The X-Noirdoc-Shield header controls Noirdoc Shield per request (X-Noirdoc-Mask remains supported as a compatible alias). With default_headers, the client sends it with every request. For a single request, set it in Python with extra_headers and in TypeScript with the headers option.
Shield statelessly inspects inbound text on Chat Completions, Responses and Embeddings, redacts personal data such as [PERSON] or [IBAN] and blocks detected secrets ([SECRET]) and prompt injections before forwarding with status code 403 (shield_violation). Responses are not reverse-translated. The gateway evaluates on and off and removes the header before forwarding the request to the provider. Whether the header takes effect depends on the settings of your organisation, API key and any attached request tags (see Set up Noirdoc Shield).
Shield does not inspect audio, image generation or file uploads via /v1/files. On /v1/images/generations, the gateway rejects X-Noirdoc-Shield: on (and X-Noirdoc-Mask: on) with masking_not_supported_for_endpoint (status code 403); on /v1/audio/* and /v1/files, it rejects enforced and X-Noirdoc-Mask: on and forwards other requests uninspected. Use a separate client without the header in default_headers for image generation.
client = OpenAI(
base_url="https://api.noirdoc.de/v1",
api_key=os.environ["NOIRDOC_API_KEY"],
default_headers={"X-Noirdoc-Shield": "on"},
)
# Turn off Shield for this request
response = client.chat.completions.create(
model="qwen3.8-27b",
messages=[{"role": "user", "content": "Hello"}],
extra_headers={"X-Noirdoc-Shield": "off"},
)const client = new OpenAI({
baseURL: "https://api.noirdoc.de/v1",
apiKey: process.env.NOIRDOC_API_KEY,
defaultHeaders: { "X-Noirdoc-Shield": "on" },
});
// Turn off Shield for this request
const response = await client.chat.completions.create(
{
model: "qwen3.8-27b",
messages: [{ role: "user", content: "Hello" }],
},
{ headers: { "X-Noirdoc-Shield": "off" } },
);Read the provider from the response
The X-Noirdoc-Provider response header names the provider that answered the request. The SDK exposes headers through the raw response: in Python with with_raw_response, in TypeScript with .withResponse().
raw = client.chat.completions.with_raw_response.create(
model="qwen3.8-27b",
messages=[{"role": "user", "content": "Hello"}],
)
print(raw.headers.get("x-noirdoc-provider"))
response = raw.parse()const { data, response } = await client.chat.completions
.create({
model: "qwen3.8-27b",
messages: [{ role: "user", content: "Hello" }],
})
.withResponse();
console.log(response.headers.get("x-noirdoc-provider"));Handle errors
Every error response from the gateway has an error object with type, code and message. The SDK exposes type and code directly on the error. In your code, check the code, not the message. The type follows the status code, for example rate_limit_error for 429.
- The gateway reports provider errors in the uniform error object: capacity problems as 429
resource_exhaustedand overload as 503provider_overloaded, each withRetry-After, rejected requests asprovider_rejected_requestwith the provider’s message, and all other errors as 502provider_error. On routes with your own provider key (BYOK), the provider’s status codes 401, 402, 403 and 429 arrive asprovider_rejected_requestwith the provider’s status code and message, as described under Error codes. TheX-Noirdoc-Providerheader names the provider that answered last. - A missing or invalid key results in status code 401 with the code
invalid_api_key. The SDK reports it asAuthenticationError. - The SDK retries requests with the status codes 408, 409, 429 and 5xx until
max_retriesis reached. It waits for the time given inRetry-Afterif the header states 60 seconds or less; otherwise it increases the wait exponentially.
If a stream breaks off, the SDK raises an APIError, see Streaming.
import openai
try:
response = client.chat.completions.create(
model="qwen3.8-27b",
messages=[{"role": "user", "content": "Hello"}],
)
except openai.APIStatusError as e:
if e.code == "provider_rejected_request":
provider = e.response.headers.get("x-noirdoc-provider")
print("Provider rejected:", provider, e.body["message"])
else:
print("Error:", e.status_code, e.code)try {
const response = await client.chat.completions.create({
model: "qwen3.8-27b",
messages: [{ role: "user", content: "Hello" }],
});
} catch (err) {
if (!(err instanceof OpenAI.APIError)) throw err;
if (err.code === "provider_rejected_request") {
const provider = err.headers?.get("x-noirdoc-provider");
console.log("Provider rejected:", provider, err.message);
} else {
console.log("Error:", err.status, err.code);
}
}Retry requests
The SDK retries failed requests itself, by default up to twice, among others for 429 and status codes from 500 upwards. For 429, 5xx and network errors, the gateway has already tried the next provider for the same model before that (failover, see Providers & routing).
A retry does not fix errors such as insufficient_credit or key_budget_exhausted (status code 402). Handle them in your code.
All codes and how to fix them are listed under Error codes.
| Status | Code | Meaning |
|---|---|---|
| 400 | wrong_endpoint_for_model | The model belongs to a different endpoint family or API format than the endpoint called. |
| 402 | insufficient_credit | The organisation's credit is used up or lower than the estimated maximum cost of the request (including Noirdoc Shield, also on BYOK routes when Shield is active). |
| 402 | key_budget_exhausted | This key's budget is used up for the current period or does not cover the estimated cost of the request. |
| 403 | model_not_allowed_for_key | The model is not one of this key's allowed models. |
| 403 | shield_violation | Noirdoc Shield blocked the request before forwarding to the provider because an active protection filter configured with block matched; message names the triggered rule (secrets, pii, ner, injection or custom). |
| 403 | masking_not_supported_for_endpoint | The endpoint supports neither Noirdoc Shield nor legacy masking, yet the policy is enforced or the request sends X-Noirdoc-Mask: on (and on /v1/images/generations also X-Noirdoc-Shield: on). During the legacy masking transition until 30.11.2026, this also affects /v1/embeddings and /v1/rerank on pre-existing organisations that have not switched to Shield yet. |
| 404 | model_not_available | The model ID is not available to the organisation. |
| 502 | provider_unreachable | The gateway could not connect to the provider, or the connection dropped. |
| 504 | provider_timeout | The provider did not answer within the gateway's timeout. The response contains Retry-After with 10 seconds. |