LiteLLM with an uncensored OpenAI-compatible endpoint

LiteLLM OpenAI-compatible endpoint setup for uncensored builds: openai/ plus the model id and api_base, in the SDK and in config.yaml, with a fallback chain.

Steps checked October 25, 2026 · prices as of October 27, 2026

A LiteLLM OpenAI-compatible endpoint takes three values: the model as openai/ plus an id, the api_base, and a key. This guide fills them with uncensored builds, first in the Python SDK, then in the proxy's config.yaml, with a fallback chain across three builds. Field names come from docs.litellm.ai, read on 25 October 2026.

If builds with the refusals edited out are new to you, start with abliterated vs uncensored, then come back here to wire them up.

How LiteLLM reaches an OpenAI-compatible endpoint

LiteLLM picks a provider from the prefix on the model name. For any server that speaks OpenAI's /chat/completions, the prefix is openai/, followed by that server's own model id. The OpenAI-compatible endpoints doc puts it in one comment: add the prefix "so litellm knows to route to OpenAI".

The same doc says to leave the base URL alone apart from the /v1 suffix. Every uncensored build in this catalog takes the OpenAI wire format, so one prefix covers all of them. Version checked: litellm 1.106.3, the current release on PyPI on 25 October 2026.

The three values LiteLLM needs

model
openai/glm-5.3-uncensored
openai/ plus any id from the models page
api_base
https://api.uncensoredllm.ai/v1
Ends in /v1; nothing after it
api_key
sk-ullm-live-…
Created in the console; keep it in ULLM_API_KEY

Create the key in the console. It starts with sk-ullm-, is shown once, and spends a prepaid balance that starts at $10. Model ids are on the uncensored model list.

Call the endpoint from the LiteLLM Python SDK

One call, no config file. The response has the OpenAI shape, so code written for the OpenAI SDK reads it the same way.

main.py
1import os
2import litellm
3
4response = litellm.completion(
5 model="openai/glm-5.3-uncensored",
6 api_base="https://api.uncensoredllm.ai/v1",
7 api_key=os.environ["ULLM_API_KEY"],
8 messages=[{"role": "user", "content": "Outline a heist thriller in five beats."}],
9 max_tokens=600,
10)
11print(response.choices[0].message.content)

Swap the id after openai/ to change builds. Nothing else in the call changes, because every build sits behind the same base URL and key.

A fallback chain across three uncensored builds

The Router takes a list of named deployments and a fallbacks map. Per the reliability doc, a call that still fails after num_retries moves to the next name, and fallbacks run in order.

router.py
1import os
2from litellm import Router
3
4BASE = "https://api.uncensoredllm.ai/v1"
5KEY = os.environ["ULLM_API_KEY"]
6
7
8def build(model_id):
9 return {"model": f"openai/{model_id}", "api_base": BASE, "api_key": KEY}
10
11
12router = Router(
13 model_list=[
14 {"model_name": "uncensored-primary", "litellm_params": build("glm-5.3-uncensored")},
15 {"model_name": "uncensored-second", "litellm_params": build("deepseek-v4-pro-uncensored")},
16 {"model_name": "uncensored-third", "litellm_params": build("glm-5.3-abliterated")},
17 ],
18 fallbacks=[{"uncensored-primary": ["uncensored-second", "uncensored-third"]}],
19 num_retries=1,
20)
21
22response = router.completion(
23 model="uncensored-primary",
24 messages=[{"role": "user", "content": "Outline a heist thriller in five beats."}],
25)
26print(response.choices[0].message.content)

Two details decide whether the chain helps. First, all three entries use the same key, so a 401 or a 402 comes back the same from every build; fallbacks cannot fix those. They help when one build fails on its own: a 404 for an id that left the catalog, a 429, a 5xx or a timeout. Second, the docs page states that a request that fails before the model answers is not charged. A request that falls through is billed once, at the price of the build that answered.

The names are aliases. Your application asks for uncensored-primary and never sees a model id, so moving to another build is one line in the list, not a change to the app. num_retries=1 gives a failing build one more try before the chain moves on. A retry that fails before the model answers is free as well, so the setting costs latency, not money.

The same chain in the LiteLLM proxy config.yaml

The proxy reads the same deployments from a file and serves them on an OpenAI-format endpoint of its own. The os.environ/ form reads a value from the environment, which keeps the key out of the file. The prices under model_info are this catalog's, written into the file when this page rendered, so LiteLLM's spend tracking matches what the API charges.

config.yaml
1model_list:
2 - model_name: uncensored-primary
3 litellm_params:
4 model: openai/glm-5.3-uncensored
5 api_base: https://api.uncensoredllm.ai/v1
6 api_key: os.environ/ULLM_API_KEY
7 model_info:
8 input_cost_per_token: 0.000000282
9 output_cost_per_token: 0.000000846
10 - model_name: uncensored-second
11 litellm_params:
12 model: openai/deepseek-v4-pro-uncensored
13 api_base: https://api.uncensoredllm.ai/v1
14 api_key: os.environ/ULLM_API_KEY
15 model_info:
16 input_cost_per_token: 0.000001
17 output_cost_per_token: 0.000002
18 - model_name: uncensored-third
19 litellm_params:
20 model: openai/glm-5.3-abliterated
21 api_base: https://api.uncensoredllm.ai/v1
22 api_key: os.environ/ULLM_API_KEY
23 model_info:
24 input_cost_per_token: 0.00000225
25 output_cost_per_token: 0.0000045
26
27router_settings:
28 fallbacks: [{"uncensored-primary": ["uncensored-second", "uncensored-third"]}]
29
30litellm_settings:
31 num_retries: 1
32 request_timeout: 120
33
34general_settings:
35 master_key: os.environ/LITELLM_MASTER_KEY
shell
1export ULLM_API_KEY="sk-ullm-live-…"
2export LITELLM_MASTER_KEY="sk-pick-your-own"
3litellm --config config.yaml

The proxy listens on port 4000. Point any OpenAI SDK at it with the master key, and ask for uncensored-primary as the model. LiteLLM's custom pricing doc marks the two *_cost_per_token keys as tracking only; they change nothing about what is billed.

Context window and content policy fallbacks

context_window_fallbacks handle LiteLLM's ContextWindowExceededError. The reliability doc requires enable_pre_call_checks: true under router_settings for that check, and without it requests go to the provider whatever their size. The check needs a window to compare against, and the three builds above publish none in the catalog. For a context fallback, add a fourth entry for minimax-m3-uncensored, which lists 262,144 tokens, and map uncensored-primary to it.

content_policy_fallbacks fire on a provider's content-filter error, LiteLLM's ContentPolicyViolationError. Mapping a filtered model on another provider to uncensored-primary sends those requests here. A refusal written into a normal reply is a successful response, not an error, so no fallback sees it.

Which uncensored builds to chain

Put the build you want most first: the backups bill only on the requests that reach them. When this guide was written, all three had been steady since 12 October on the daily real-model check, which sends a fixed prompt to every model each day and records whether the answer still matches. The column below is today's verdict.

Uncensored models and their prices per million tokens
Wire formats
GLM-5.3 Uncensoredglm-5.3-uncensoredUncensored$0.282$0.846openai
DeepSeek V4 Pro Uncensoreddeepseek-v4-pro-uncensoredUncensored$1.00$2.00openai
GLM-5.3 Abliteratedglm-5.3-abliteratedAbliterated$2.25$4.50openai

Prices per million tokens, read from the live catalog on 2026-10-27.

Model idRole in config.yamlDaily checkContext window
glm-5.3-uncensoreduncensored-primarySteady since Oct 12not published
deepseek-v4-pro-uncensoreduncensored-secondSteady since Oct 12not published
glm-5.3-abliterateduncensored-thirdSteady since Oct 27not published

Windows from the live catalog on 2026-10-27; verdicts from /proof at render.

Two GLM builds and a DeepSeek build means a family outage on one side still leaves an answer. The rest of each family is on the uncensored GLM and uncensored DeepSeek pages.

What a batch through the chain costs

The worked example: 10,000 requests of 1,200 prompt tokens and 350 reply tokens, all answered by one build.

Model10,000 requests
GLM-5.3 Uncensored$6.35
DeepSeek V4 Pro Uncensored$19.00
GLM-5.3 Abliterated$42.75

Computed at render from the catalog prices on 2026-10-27: requests × 1,200 × input price, plus requests × 350 × output price.

A real month sits close to the first row, plus a backup's price on the requests that fall through to it. Every per-token rate is on the pricing page.

Check the key before LiteLLM

This lists the ids the key can call. It is free; run it before blaming the config.

curl
1curl https://api.uncensoredllm.ai/v1/models \
2 -H "Authorization: Bearer sk-ullm-live-…"

A 200 with a list of ids means the key and base URL are right. A 401 says which way the key is wrong:

http
1HTTP 401
2{"error":{"message":"No API key was sent.","type":"authentication_error","param":null,"code":"invalid_api_key"}}
http
1HTTP 401
2{"error":{"message":"The API key is not valid.","type":"authentication_error","param":null,"code":"invalid_api_key"}}

Both bodies as the API returned them on 25 October 2026.

Troubleshooting LiteLLM errors on this endpoint

LiteLLM maps each status to an exception class, per its exception mapping doc: 401 is AuthenticationError, 402 is PaymentRequiredError, 404 is NotFoundError, 429 is RateLimitError.

Not Found on every call

The OpenAI-compatible doc's advice: make sure api_base ends in /v1. Check also that the model name starts with openai/.

404 model_not_found on one build

The id after openai/ is not in the catalog. The chain moves on to the next build; fix the id from the list the curl above prints.

http
1HTTP 404
2{"error":{"message":"No such model: glm-5.3-uncensord.","type":"not_found_error","param":null,"code":"model_not_found"}}

402 insufficient_balance

The prepaid balance is at zero, so every build in the chain answers the same. Top up in the console; a refused request is not charged.

http
1HTTP 402
2{"error":{"message":"The prepaid balance does not cover this request. Top up at … to continue.","type":"insufficient_quota","param":null,"code":"insufficient_balance"}}

Questions

Both ways. It calls OpenAI-compatible endpoints when the model name starts with openai/, and its proxy serves an OpenAI-format endpoint of its own on port 4000.

An API that takes the same requests and returns the same responses as OpenAI's /v1/chat/completions, so a client needs only a base URL, a key and a model id. This one is https://api.uncensoredllm.ai/v1. See the docs page.

Set model to openai/ plus the provider's model id, api_base to its URL ending in /v1, and api_key to its key. Here that is openai/glm-5.3-uncensored and https://api.uncensoredllm.ai/v1.

Services that answer the OpenAI wire format at their own base URL. LiteLLM’s doc also names provider-specific prefixes, such as hosted_vllm, for servers that match one; this API takes plain openai/. See the uncensored model list.

Yes. The proxy speaks the OpenAI format, so the OpenAI Node SDK works with its baseURL set to the proxy and the master key as apiKey.

Only if it knows the prices. The config above writes this catalog’s per-token prices into model_info, which LiteLLM uses for tracking only. See per-token prices.

Get a keyPrepaid from $10, no subscription.

New models and price changes, once a month.

New uncensored builds with their prices, price moves, and what the daily model check found.