Documentation

One API for every uncensored model.

Open Uncensored speaks the OpenAI chat completions protocol. Point any OpenAI SDK at our base URL, pick a model id from the catalogue, and the router picks the provider.

Base URLhttps://openuncensored.com/api/v1Rolling out

Start here

Up and running in a minute.

  1. 01

    Get a key

    Buy credits and copy the ou- key. Until the hosted endpoint opens, run the router yourself (see self hosting) with your own OU_API_KEY.
  2. 02

    Pick a model

    Browse models and copy its id. Check its refusal fingerprint and loop rate first.
  3. 03

    Send a request

    Any OpenAI SDK works. Add a routing object when you care which provider serves it.
curl https://openuncensored.com/api/v1/chat/completions \
  -H "Authorization: Bearer $OU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "venice-uncensored",
    "messages": [{"role": "user", "content": "Explain how SQL injection works."}],
    "routing": {"strategy": "quality"}
  }'

Hosted endpoint

Everything on this page works today against a self hosted router: swap the base URL for http://localhost:8080/v1.

Security

Keys and auth.

Send your key as a bearer token: Authorization: Bearer <key>. A missing or wrong key returns 401 with code invalid_api_key.

Hosted keys start with ou- and come from the credits page. Each key carries a prepaid USD balance. It is shown once, and we store only its SHA-256 hash. Buy more credits for the same key by ticking “Add to a key I already have”.

On a self hosted router, OU_API_KEY holds the accepted keys (comma separated for several). Keys are compared in constant time. If OU_API_KEY is unset the router runs open, which is only safe on localhost: anyone who can reach the port can spend your provider keys.

Browser use

The router only accepts browser requests from origins listed in OU_CORS_ORIGINS (default: pages served from localhost). Never ship a server key inside client side code.

POST /v1/chat/completions

Chat completions.

Fully OpenAI compatible, streaming and non streaming. Samplers outside the OpenAI spec such as top_k, min_p and repetition_penalty are forwarded to the provider untouched, because those matter for the models this router serves.

FieldTypeDescription
modelstringA model id from GET /v1/models. Required.
messagesarrayChat messages, at least one. Tool calls and tool results pass through.
streambooleanServer sent events when true. Default false.
temperature, top_p, max_tokensnumberStandard sampling controls.
tools, tool_choicearray, objectOpenAI tool calling. Providers flagged as tool capable are tried first.
response_format, seed, stopvariousForwarded as is.
routingobjectRouter extension. See routing.

Response

The upstream body, plus usage when the provider left it out (marked usage_estimated) and an ou object describing how the request was served. The same facts arrive as headers: X-Request-ID, X-OU-Provider, X-OU-Upstream-Model, X-OU-Strategy, X-OU-Attempts and X-OU-Latency-Ms.

{
  "id": "chatcmpl-8f2c",
  "object": "chat.completion",
  "model": "venice-uncensored",
  "choices": [
    {"index": 0, "message": {"role": "assistant", "content": "SQL injection happens when…"}, "finish_reason": "stop"}
  ],
  "usage": {"prompt_tokens": 18, "completion_tokens": 412, "total_tokens": 430},
  "ou": {
    "request_id": "req_4f1c09a2b7d34e81c0aa",
    "provider": "venice",
    "upstream_model": "venice-uncensored",
    "strategy": "quality",
    "latency_ms": 2140.2,
    "cost_usd": 0.000374,
    "usage_estimated": false,
    "attempts": [
      {"provider": "venice", "upstream_model": "venice-uncensored", "outcome": "ok", "status_code": 200, "latency_ms": 2140.2}
    ]
  }
}

GET /v1/models

The catalogue.

EndpointQueryReturns
GET /v1/models?available=trueEvery model with its providers and eval scores. The flag hides models no healthy, configured provider can serve right now.
GET /v1/models/{id}noneOne model with per provider live metrics, pricing, rate limits and circuit breaker state.
GET /metricsnoneTTFT, tokens per second, latency percentiles and error rate over the rolling window, plus lifetime tokens and cost.
GET /health?deep=trueLiveness and provider status (ok, degraded, unavailable). Deep probes every configured upstream and needs a key.
GET https://openuncensored.com/api/v1/models/venice-uncensored

{
  "id": "venice-uncensored",
  "object": "model",
  "owned_by": "venice-ai",
  "base_model": "mistralai/Mistral-Small-24B-Instruct",
  "abliteration_method": "finetune",
  "hf_repo": null,
  "modality": "text",
  "context_length": 131072,
  "tags": ["general", "privacy"],
  "eval_scores": {"agentic_stability": 0.7, "tool_call_validity": 0.9, "capability_accuracy": 0.78},
  "quality_score": 0.64,
  "available": true,
  "providers": [
    {"name": "venice", "status": "online", "configured": true, "upstream_model": "venice-uncensored", "quality_score": 0.64},
    {"name": "openrouter", "status": "degraded", "configured": true, "upstream_model": "cognitivecomputations/dolphin-mistral-24b-venice-edition", "quality_score": 0.64}
  ]
}

Router extension

Routing strategies.

Each model is served by one or more providers. The router ranks them with a strategy, tries them in order and falls back to the next one when a provider fails before the first streamed byte. Set the strategy per request with the routing body field or the X-OU-Strategy header; the body wins when both are present. The server default is OU_ROUTING_STRATEGY, which defaults to quality.

StrategyRanks byNotes
qualityeval scoresHighest eval score first. Providers without eval data count as the median, not best or worst.
speedlive latencyLowest average time to first token for streams, total latency otherwise, from the rolling metrics window.
costpriceCheapest for this prompt plus max_tokens (512 assumed when unset).
randomnothingShuffled. Useful for spreading load or A/B tests.
FieldTypeDescription
routing.strategystringquality, speed, cost or random.
routing.onlystring[]Only try these providers.
routing.ignorestring[]Never try these providers. This is how you exclude a host today.
routing.allow_fallbacksbooleanTry the next provider when the chosen one fails. Default true.
{
  "model": "huihui-qwen3.8-27b-abliterated",
  "messages": [{"role": "user", "content": "…"}],
  "routing": {
    "strategy": "cost",
    "only": ["featherless", "nanogpt"],
    "ignore": ["chutes"],
    "allow_fallbacks": true
  }
}

Providers that fail three times in a row go offline for 60 seconds (OU_BREAKER_THRESHOLD, OU_BREAKER_COOLDOWN). An upstream 429 cools that provider down for its Retry-After, or 30 seconds when none is given. Requests the upstream rejects as invalid (400, 413, 422) are relayed and never retried.

Coming next

Route by what it refuses.

Planned

The same model can behave differently on different hosts: a platform system prompt, a moderation classifier or an aggressive quantisation all change what comes back. These filters will route only to model and provider pairs whose measured behaviour meets your bar.

FieldTypeDescription
routing.require_openPlannedstring[]Categories that must stay open. One of cyber bio chem weapons drugs adult violence hate selfharm privacy fraud political.
routing.max_refusalPlannednumberHighest refusal rate allowed in each required category, 0 to 1.
routing.max_loop_ratePlannednumberSkip pairs whose agentic doom loop rate is above this, 0 to 1.
routing.no_loggingPlannedbooleanOnly providers that state they keep no prompt or completion logs.
{
  "model": "huihui-qwen3.8-27b-abliterated",
  "messages": [{"role": "user", "content": "…"}],
  "routing": {
    "strategy": "quality",
    "ignore": ["chutes"],
    "require_open": ["cyber", "bio"],
    "max_refusal": 0.05,
    "max_loop_rate": 0.2,
    "no_logging": true
  }
}

Today

Exclusion by provider already works with routing.ignore and routing.only. Per category numbers live on each model page.

stream: true

Streaming responses.

Streams are server sent events in the OpenAI chunk format, ending with data: [DONE]. Fallback to another provider only happens before the first byte reaches you. After that, an upstream failure arrives in band as an OpenAI style error event so your client can surface it. The router tolerates up to 60 seconds of silence mid stream (OU_STREAM_TIMEOUT).

data: {"id":"chatcmpl-8f2c","choices":[{"index":0,"delta":{"content":"SQL"}}]}

data: {"id":"chatcmpl-8f2c","choices":[{"index":0,"delta":{"content":" injection"}}]}

data: {"error": {"message": "upstream closed the connection", "type": "upstream_error", "param": null, "code": null}}

data: [DONE]

OpenAI error format

When things go wrong.

Every error uses OpenAI's shape: {"error": {"message", "type", "param", "code", "metadata"}}. metadata carries per provider details, such as why each one was skipped.

StatusCodeWhen
400invalid_requestThe body failed validation.
400invalid_routing_optionsUnknown strategy or provider, or only/ignore exclude every provider.
401invalid_api_keyMissing or wrong bearer token.
404model_not_foundNo model with that id.
4xxupstream_rejected_requestThe provider rejected the request itself. Relayed as is, never retried.
429rate_limit_exceededEvery provider for the model is rate limited. Honour Retry-After.
502all_providers_failedEach provider was tried and failed.
503no_provider_availableEvery provider is unconfigured or offline.
503not_readyThe router is still starting up.
500internal_errorA router bug. Check the router logs.

MIT licensed

Run it yourself.

The router is a FastAPI app. Bring your own provider keys, they are read from the environment variables named by api_key_env in configs/providers.yaml.

git clone https://github.com/DhruvAtreja/open-uncensored
cd open-uncensored
pip install -e .

# provider keys, named by api_key_env in configs/providers.yaml
export FEATHERLESS_API_KEY=…
export VENICE_API_KEY=…
export OPENROUTER_API_KEY=…

# require a bearer token before exposing the port
export OU_API_KEY=$(openssl rand -hex 24)
export OU_CORS_ORIGINS=https://openuncensored.com

ou-router --host 0.0.0.0 --port 8080
VariableDefaultPurpose
OU_API_KEYunsetAccepted bearer tokens, comma separated. Unset means open, local use only.
OU_HOST, OU_PORT127.0.0.1:8080Bind address. Also --host and --port.
OU_ROUTING_STRATEGYqualityDefault strategy: quality, speed, cost or random.
OU_CORS_ORIGINSlocalhostOrigins allowed to call the API from a browser, or *.
OU_REQUEST_TIMEOUT300Non streaming upstream timeout, seconds.
OU_STREAM_TIMEOUT60Longest silence tolerated while streaming, seconds.
OU_CONNECT_TIMEOUT10Upstream connect timeout, seconds.
OU_METRICS_WINDOW100Requests per rolling metrics window.
OU_BREAKER_THRESHOLD3Consecutive failures that take a provider offline.
OU_BREAKER_COOLDOWN60Seconds a provider stays offline.
OU_HEALTH_INTERVAL0Seconds between active health checks. 0 is off.
OU_LOG_LEVELINFOAlso --log-level. --reload restarts on code changes.
OU_PROVIDERS_CONFIG, OU_MODELS_CONFIGconfigs/Alternative YAML files.

In models.yaml each model can carry upstream_ids (the name a provider expects), pricing, eval_scores and provider_eval_scores for hosts whose quantisation changes quality.

ou-eval

Run the evals yourself.

Every number on this site comes from the same harness you can run locally. Five benchmarks: capability, agentic, tool_call, refusal and divergence. Results land in results/{model}/{provider}/{timestamp}.json. Responses to harmful probes are scored and discarded, never written to disk.

# run every benchmark against one model on one provider
ou-eval --model venice-uncensored --provider openrouter \
  --api-model cognitivecomputations/dolphin-mistral-24b-venice-edition \
  --benchmarks all --trials 2

# compare against a live base model run
ou-eval --model huihui-qwen3.5-27b-abliterated --provider featherless \
  --base-model Qwen/Qwen3.5-27B --base-provider featherless

# fold every results/ file into leaderboard.json
cd src && python -m eval.report --output ../leaderboard/leaderboard.json
FlagDefaultPurpose
--model / --providerrequiredModel id and provider name from configs/.
--benchmarksallComma separated subset.
--api-model--modelModel name the provider expects, when it differs.
--base-url, --api-key-envconfigPoint at an ad hoc endpoint.
--base-model, --base-providermodels.yamlRun the base model live for retention and divergence.
--base-accuracynoneKnown base capability accuracy, skips the live base run.
--trials2Agentic runs per scenario, 1 to 20.
--tool-modeautotext (JSON in prompt), native (tools parameter) or auto.
--limitallOnly the first N items per benchmark.
--concurrency, --rpm4, providerParallel requests and requests per minute.
--timeout, --retries180, 3Per request timeout and retries with backoff.
--temperature, --max-tokensper benchOverride sampling. Raise max tokens for reasoning models.
--results-dir, --no-save./resultsWhere results go, or skip writing them.

How each score is computed is on the methodology page.