Key points
- The label covers URL paths, JSON request and response shapes, Bearer auth and SSE streaming.
- It says nothing about which model answers, how it behaves, or what it costs.
- Switching usually means changing two values: base URL and API key.
- Test streaming, tools, JSON mode and error codes before you rely on a provider.
The short answer
When a provider says its API is openai-compatible, it means that it accepts the same HTTP requests the OpenAI client libraries send and returns responses in the same shape. You keep your code, change the base URL and the key, and the call goes to a different server.
That is the whole promise. It is a statement about the interface, like saying two chargers use the same plug. It does not tell you what is on the other end of the cable. The model, its safety rules, its price and its limits are all decided by the provider, and they differ a lot.
What the label actually covers
In practice, OpenAI compatibility is a short list of concrete things. If a provider supports all of them, most client code runs unchanged.
- Paths. Chat requests go to
POST /v1/chat/completionsand the model list comes fromGET /v1/models. - Auth. The key travels in an
Authorization: Bearerheader. - Request body. A
modelstring, amessagesarray of objects withroleandcontent, and optional sampling fields such astemperature,top_p,stopandseed. - Response body. A
choicesarray with the assistant message, afinish_reason, and ausageobject with token counts. - Streaming. With
stream: truethe server sends Server-Sent Events, each carrying a small JSON delta, ending with a final marker. - Optional features. Tool calling, JSON mode and similar fields, where the provider chooses to support them.
Because so many tools were written against this format, it became a common target. Editors, agent frameworks, roleplay front ends and plain scripts all let you enter a custom base URL, and that is all they need.
Why one format became the common target
The chat completions format spread because it was simple and arrived early. A tool author who supports it once can reach many backends, and a backend author who supports it once can reach many tools. That is a good deal for both sides, so the format stuck, even among services that have nothing to do with the company that designed it.
For you as a developer this has a practical meaning. Your code does not need a different integration for every provider. You can move a prototype between endpoints in an afternoon, compare two models on the same prompts, or keep a second provider as a fallback when the first one is slow. The cost of changing your mind is low, and that makes it easier to try a new service without committing to it.
The flip side is that the shared surface hides real differences. Because switching is so easy, it is tempting to assume everything behind the URL is equivalent. It is not, and the sections below show where the gaps usually appear.
What it does not tell you
Here is where people get surprised. The label leaves out everything that determines whether the API is useful for your work.
The model behind it
A compatible endpoint can serve any model. Some serve open-weight models, some route to several vendors, some wrap a proprietary model. Compatibility does not mean the service is run by OpenAI, and it does not mean the model behaves like one of theirs. The model name you pass in model is just a string the provider defines.
Refusal behavior
Content rules belong to the model and the provider, not to the protocol. Two compatible APIs can answer the same prompt in opposite ways. If your use case is fiction, roleplay or other lawful adult content, check what the provider says about refusals before you wire anything up.
Feature depth
Some providers implement chat only. Others add tools, JSON mode, log probabilities or extra endpoints. A client that sends a field the server ignores will not always fail loudly, so a silent mismatch is possible.
Limits, pricing and errors
Rate limits, context length, maximum body size and price per token are all provider choices. Error bodies also differ in detail even when the HTTP status codes match, so retry logic should key on status codes and headers rather than on message text.
A quick compatibility test
You can verify a provider in a few minutes with three requests. Replace the key with your own. This example uses the base URL of this site.
curl https://api.kimicode.top/v1/models \
-H "Authorization: Bearer YOUR_API_KEY"
curl https://api.kimicode.top/v1/chat/completions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "uncensored",
"messages": [{"role": "user", "content": "Reply with the word ready."}]
}'
The first call confirms the key and lists model IDs. The second checks the chat shape. Then test the parts your application depends on: add "stream": true and watch for incremental chunks, pass a tools array if you use function calling, and set response_format to {"type":"json_object"} if you need parseable output.
Also trigger an error on purpose, such as a bad key. You want to see a clean HTTP status and a JSON error body your client can handle.
The same call in Node
The JavaScript SDK takes the same two values. Here is a streaming call, which is the shape most chat interfaces use.
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api.kimicode.top/v1",
apiKey: process.env.API_KEY,
});
const stream = await client.chat.completions.create({
model: "uncensored",
stream: true,
messages: [{ role: "user", content: "Name three uses of SSE." }],
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
If this prints text word by word, the streaming path works end to end. The same loop works against any compatible provider, which is the entire point of the format.
What switching looks like in code
With the official Python SDK, the change is two constructor arguments. The rest of your program stays as it was.
from openai import OpenAI
client = OpenAI(
base_url="https://api.kimicode.top/v1",
api_key="YOUR_API_KEY",
)
resp = client.chat.completions.create(
model="uncensored",
messages=[{"role": "user", "content": "Summarize SSE in one sentence."}],
)
print(resp.choices[0].message.content)
The Node SDK is the same idea with baseURL and apiKey options. Many tools read OPENAI_BASE_URL and OPENAI_API_KEY from the environment, so you can sometimes switch without touching code at all.
Remember to change the model string too. A request that names a model the new server does not host will fail, and that is the most common first error.
Where this site's API fits
This service is a hosted, text-only API that follows the OpenAI format. It runs one open-weight model, operated by an independent provider and tuned to answer without content refusals for lawful adult use. It is not the official API of any model vendor and it does not route between vendors. The model ID is uncensored.
It supports streaming over SSE with usage reported at the end, tool calling, JSON mode, and passes through temperature, top_p, stop, seed and the penalties. The context window is 100,000 tokens for prompt and completion together. It does not generate images or audio, and it does not offer embeddings or fine-tuning. One rule is fixed: no sexual content involving minors.
Billing is prepaid and metered: $0.15 per 1M input tokens and $0.50 per 1M output tokens, with no subscription. You can see the numbers on the pricing page, and the docs walk through a first request. If you want to try it without paying, new accounts can claim a $0.50 trial credit with one click on the API key page.
Common compatibility pitfalls
- Wrong base URL. The URL should end at
/v1. Clients append/chat/completionsthemselves, so adding it to the base produces a 404. - Unsupported parameters. If a library sends a field the server does not know, check whether it is rejected or silently dropped, then remove it.
- Streaming and concurrency. A streamed request holds a concurrency slot until the stream ends. If your app opens many streams, you can hit a 429 with code
concurrencyeven though your per-minute count is low. - Retry on 429. Treat 429 as a signal to wait. When a
Retry-Afterheader is present, honor it. - Token counting. Do not assume another provider's tokenizer matches. Read the
usageobject in the response for the numbers you are billed on.
None of these are hard to fix once you know to look for them, and most come down to reading the first error message carefully.
