The three values
- Base URL
https://nokeep.ai/v1- API key
sk-nokeep-test (free) or your sk-nokeep-… key- Model
deepseek-v4.1-flash-uncensored
The official OpenAI SDKs read the base URL and the key from the environment, so for many projects the switch needs no code change at all:
export OPENAI_BASE_URL="https://nokeep.ai/v1"
export OPENAI_API_KEY="sk-nokeep-…"Then change the model name where you call it. Every other line stays.
In Python, JavaScript and curl
import os
from openai import OpenAI
client = OpenAI(
base_url="https://nokeep.ai/v1",
api_key=os.environ["NOKEEP_API_KEY"],
)
reply = client.chat.completions.create(
model="deepseek-v4.1-flash-uncensored",
messages=[{"role": "user", "content": "Write a haiku about moving house."}],
)
print(reply.choices[0].message.content)Streaming, tools, JSON output and pictures work as they do with OpenAI’s own format. Libraries and frameworks built on the OpenAI SDK take the same base URL and key. Try the call right here first:
What’s supported
| Feature | Supported |
|---|---|
POST /v1/chat/completions, whole or streamed | Yes, both models |
GET /v1/models and GET /v1/models/{id} | Yes, with context length, longest reply and price per token |
| Roles: system, developer, user, assistant, tool | Yes |
Tool calls: tools, tool_choice | Yes. On GLM, tool_choice takes auto or none only. |
reasoning_effort | DeepSeek: none, low, high, max. GLM: low, high. |
Pictures as image_url parts, links or data URLs | deepseek-v4.1-flash-uncensored, up to 10 per request |
response_format, temperature, top_p, stop, seed, penalties | deepseek-v4.1-flash-uncensored |
stream_options.include_usage, max_tokens, max_completion_tokens | Yes |
Cached input in usage.prompt_tokens_details.cached_tokens | Yes, billed at the cached price |
Every field, model by model: Chat completions.
What isn’t
| Not supported | What you get |
|---|---|
| Other endpoints: embeddings, images, audio, files, batch, moderations, the Responses and Assistants APIs, legacy completions | 404 not_found, naming the four routes there are |
n other than 1 | 400 unsupported_n |
A reasoning_effort the model doesn’t take | 400 invalid_reasoning_effort, naming the ones it does |
A named function or required as tool_choice on glm-5.3-flash-uncensored | 400 unsupported_tool_choice |
Pictures to glm-5.3-flash-uncensored; audio or video parts to either model | 400 unsupported_input, naming the model that reads pictures |
| More than 10 pictures, or more than 2,000 messages | 400 too_many_images, 400 invalid_messages |
Fields neither model takes, such as logprobs, logit_bias, user or store | Dropped, and named in the x-nokeep-ignored header |
Nothing unsupported is guessed at or changed quietly: a request is either refused with a code, or its unused fields are named in a header. Every code and its fix: Errors.
Small differences to know
usage.nokeep_creditssays what the call cost, and thex-nokeep-creditsandx-nokeep-balanceheaders say it on a whole reply.- Billing is from prepaid credits: 1,000 are $1, from $5, and they never expire. A refused call costs nothing.
- Rate limit: 120 requests a minute per key. A
429carriesRetry-After. - GLM’s host can be busy at peak times; a busy call answers
503 busywithRetry-After: 5and costs nothing.
Questions
Do I need to change my code to switch?
Only the base URL, the key and the model name. With the official SDKs, the first two can come from OPENAI_BASE_URL and OPENAI_API_KEY in the environment.
Does it work with apps that take a custom OpenAI endpoint?
Yes. Any app with an OpenAI-compatible or custom endpoint setting works. Guides: SillyTavern, Open WebUI, and coding agents on the coding agents page.
Which OpenAI models can I call?
None: the format is OpenAI’s, the models are deepseek-v4.1-flash-uncensored and glm-5.3-flash-uncensored. GET /v1/models lists every id.
Is there an embeddings endpoint?
Not yet. The API serves chat completions, the model list and a status route. New routes will appear in the changelog.
What happens to fields you don’t support?
They are dropped and named in the x-nokeep-ignored header, so you can see exactly what wasn’t used. Values that would change the meaning of a request, such as n above 1, are refused with a 400 instead.