[{"data":1,"prerenderedAt":93},["ShallowReactive",2],{"guides-python-uncensored-llm-api":3},{"collection":4,"slug":5,"path":6,"title":7,"seoTitle":8,"description":9,"summary":10,"primaryKeyword":11,"cluster":12,"published":13,"updated":13,"reviewed":13,"readingMinutes":14,"html":15,"toc":16,"wordCount":39,"sources":40,"faq":57,"related":67,"tested":84,"internalLinks":88},"guides","python-uncensored-llm-api","\u002Fguides\u002Fpython-uncensored-llm-api\u002F","Call an uncensored LLM API from Python","Uncensored LLM API in Python: Setup, Streaming, Errors","Call an uncensored or open-weight LLM from Python with the OpenAI SDK: environment variables, first request, streaming, error handling, retries and provider switching.","Install the openai package, set LLM_BASE_URL, LLM_API_KEY and LLM_MODEL, and call client.chat.completions.create. The same code works with any OpenAI-compatible provider or a local server; only the three environment variables change. Handle timeouts and status errors explicitly, and read usage to see what you were billed.","uncensored llm api python","quickstart","2026-11-14",3,"\u003Ch2 id=\"what-you-need\">What you need\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>Python 3.10 or newer, which the current openai package requires. The examples were run on Python 3.12.\u003C\u002Fli>\n\u003Cli>An API key from a provider, or a local OpenAI-compatible server such as llama.cpp’s \u003Ccode>llama-server\u003C\u002Fcode>, vLLM or Ollama. A local server does not need a real key, but the SDK requires a non-empty string.\u003C\u002Fli>\n\u003Cli>The model id the provider expects. It is not always the Hugging Face repo name: compare the ids on our \u003Ca href=\"\u002Fapi\u002Fproviders\u002F\">provider pages\u003C\u002Fa>.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>Install the SDK in a virtual environment:\u003C\u002Fp>\n\u003Cdiv class=\"code-block\" data-lang=\"bash\">\u003Cspan class=\"sr-only\">bash code\u003C\u002Fspan>\u003Cpre tabindex=\"0\">\u003Ccode class=\"language-bash\">python -m venv .venv\nsource .venv\u002Fbin\u002Factivate        # Windows: .venv\\Scripts\\activate\npython -m pip install openai\n\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\n\u003Ch2 id=\"keep-the-key-out-of-the-code\">Keep the key out of the code\u003C\u002Fh2>\n\u003Cp>Put the three values in the environment, not in the script. That way the same file runs against any provider and never leaks a key into version control.\u003C\u002Fp>\n\u003Cdiv class=\"code-block\" data-lang=\"bash\">\u003Cspan class=\"sr-only\">bash code\u003C\u002Fspan>\u003Cpre tabindex=\"0\">\u003Ccode class=\"language-bash\">export LLM_BASE_URL=&quot;https:\u002F\u002Fapi.featherless.ai\u002Fv1&quot;\nexport LLM_API_KEY=&quot;your-key-here&quot;\nexport LLM_MODEL=&quot;huihui-ai\u002FHuihui-Qwen3.8-27B-abliterated&quot;\n\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\n\u003Cp>On Windows PowerShell use \u003Ccode>$env:LLM_API_KEY = &quot;...&quot;\u003C\u002Fcode>. In production, use your platform’s secret store. The base URL and model above are one example; the \u003Ca href=\"\u002Fapi\u002F\">API overview\u003C\u002Fa> lists the documented base URL for each provider.\u003C\u002Fp>\n\u003Ch2 id=\"first-request\">First request\u003C\u002Fh2>\n\u003Cdiv class=\"code-block\" data-lang=\"python\">\u003Cspan class=\"sr-only\">python code\u003C\u002Fspan>\u003Cpre tabindex=\"0\">\u003Ccode class=\"language-python\">&quot;&quot;&quot;One chat completion against any OpenAI-compatible API.\n\nSet LLM_BASE_URL, LLM_API_KEY and LLM_MODEL in the environment first.\n&quot;&quot;&quot;\nimport os\nimport sys\n\nfrom openai import OpenAI, APIConnectionError, APIStatusError, APITimeoutError\n\nclient = OpenAI(\n    base_url=os.environ[&quot;LLM_BASE_URL&quot;],\n    api_key=os.environ[&quot;LLM_API_KEY&quot;],\n    timeout=60.0,      # seconds for the whole request\n    max_retries=2,     # the SDK retries 408, 409, 429 and 5xx with backoff\n)\n\ntry:\n    reply = client.chat.completions.create(\n        model=os.environ[&quot;LLM_MODEL&quot;],\n        messages=[\n            {&quot;role&quot;: &quot;system&quot;, &quot;content&quot;: &quot;You are a concise assistant for developers.&quot;},\n            {&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: &quot;Explain what a race condition is in two sentences.&quot;},\n        ],\n        max_tokens=300,\n    )\nexcept APITimeoutError:\n    sys.exit(&quot;The request timed out. Try a smaller max_tokens or a longer timeout.&quot;)\nexcept APIConnectionError as e:\n    sys.exit(f&quot;Could not reach {os.environ['LLM_BASE_URL']}: {e}&quot;)\nexcept APIStatusError as e:\n    # 401 bad key, 404 wrong base URL or model, 413 prompt too long, 429 rate limit\n    sys.exit(f&quot;The API returned {e.status_code}: {e.message}&quot;)\n\nchoice = reply.choices[0]\nprint(choice.message.content)\nprint(f&quot;finish_reason={choice.finish_reason}&quot;)\nif reply.usage:\n    print(f&quot;tokens: {reply.usage.prompt_tokens} in, {reply.usage.completion_tokens} out&quot;)\n\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\n\u003Cp>The request has the usual chat format: a list of messages with roles \u003Ccode>system\u003C\u002Fcode>, \u003Ccode>user\u003C\u002Fcode> and \u003Ccode>assistant\u003C\u002Fcode>. The SDK sends \u003Ccode>Authorization: Bearer &lt;key&gt;\u003C\u002Fcode> and posts to \u003Ccode>&lt;base URL&gt;\u002Fchat\u002Fcompletions\u003C\u002Fcode>.\u003C\u002Fp>\n\u003Cp>Three details are worth knowing:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>\u003Ccode>finish_reason\u003C\u002Fcode>\u003C\u002Fstrong> tells you why the reply ended: \u003Ccode>stop\u003C\u002Fcode> for a natural end, \u003Ccode>length\u003C\u002Fcode> when it hit \u003Ccode>max_tokens\u003C\u002Fcode> or the provider’s output cap. A \u003Ccode>length\u003C\u002Fcode> reply is cut off; do not treat it as complete.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>\u003Ccode>usage\u003C\u002Fcode>\u003C\u002Fstrong> reports the tokens billed. Use real numbers from here in the \u003Ca href=\"\u002Ftools\u002Ftoken-cost-calculator\u002F\">token cost calculator\u003C\u002Fa> instead of guessing.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Retries.\u003C\u002Fstrong> \u003Ccode>max_retries=2\u003C\u002Fcode> lets the SDK retry rate limits (429) and server errors with backoff. In our test the mock server returned 429 twice and the third attempt succeeded without any code of ours.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2 id=\"streaming\">Streaming\u003C\u002Fh2>\n\u003Cp>For anything longer than a sentence or two, stream the reply so the user sees text as it is generated. Thinking models in particular can spend many seconds reasoning before the first visible token.\u003C\u002Fp>\n\u003Cdiv class=\"code-block\" data-lang=\"python\">\u003Cspan class=\"sr-only\">python code\u003C\u002Fspan>\u003Cpre tabindex=\"0\">\u003Ccode class=\"language-python\">&quot;&quot;&quot;Stream a reply token by token, then print the usage if the server sends it.&quot;&quot;&quot;\nimport os\n\nfrom openai import OpenAI\n\nclient = OpenAI(base_url=os.environ[&quot;LLM_BASE_URL&quot;], api_key=os.environ[&quot;LLM_API_KEY&quot;], timeout=60.0)\n\nstream = client.chat.completions.create(\n    model=os.environ[&quot;LLM_MODEL&quot;],\n    messages=[{&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: &quot;Write a haiku about version control.&quot;}],\n    max_tokens=200,\n    stream=True,\n    stream_options={&quot;include_usage&quot;: True},  # some servers ignore this; usage may be absent\n)\n\nparts = []\nusage = None\ntry:\n    for chunk in stream:\n        if chunk.usage:\n            usage = chunk.usage\n        if not chunk.choices:\n            continue\n        delta = chunk.choices[0].delta\n        if delta.content:\n            parts.append(delta.content)\n            print(delta.content, end=&quot;&quot;, flush=True)\nfinally:\n    stream.close()  # releases the connection even if you stop early\n\nprint()\nif usage:\n    print(f&quot;tokens: {usage.prompt_tokens} in, {usage.completion_tokens} out&quot;)\nprint(f&quot;characters received: {len(''.join(parts))}&quot;)\n\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\n\u003Cp>\u003Ccode>stream_options={&quot;include_usage&quot;: True}\u003C\u002Fcode> asks for a final chunk with token counts. Not every server honours it: some always send usage, some ignore the option, and the chunk carrying usage has an empty \u003Ccode>choices\u003C\u002Fcode> list, which is why the loop checks \u003Ccode>chunk.choices\u003C\u002Fcode> first. The \u003Ca href=\"\u002Fguides\u002Fstreaming-llm-responses\u002F\">streaming guide\u003C\u002Fa> explains the event format and how to cancel a stream.\u003C\u002Fp>\n\u003Ch2 id=\"errors-you-will-meet\">Errors you will meet\u003C\u002Fh2>\n\u003Cdiv class=\"table-wrap\" role=\"region\" aria-label=\"Scrollable table\" tabindex=\"0\">\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Status\u003C\u002Fth>\n\u003Cth>Usual cause\u003C\u002Fth>\n\u003Cth>What to do\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>401\u003C\u002Ftd>\n\u003Ctd>Missing or wrong key\u003C\u002Ftd>\n\u003Ctd>Check the environment variable is set in the process that runs the code\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>404\u003C\u002Ftd>\n\u003Ctd>Wrong base URL path or unknown model id\u003C\u002Ftd>\n\u003Ctd>Compare the base URL with the provider’s docs; list models\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>413 or 400\u003C\u002Ftd>\n\u003Ctd>Prompt plus output exceed the context limit\u003C\u002Ftd>\n\u003Ctd>Shorten the input or reserve less output; see the \u003Ca href=\"\u002Ftools\u002Fcontext-window-planner\u002F\">context window planner\u003C\u002Fa>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>429\u003C\u002Ftd>\n\u003Ctd>Rate limit or quota\u003C\u002Ftd>\n\u003Ctd>Let the SDK retry; if it persists, slow down or check your plan\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>503\u003C\u002Ftd>\n\u003Ctd>Model not available on the provider right now\u003C\u002Ftd>\n\u003Ctd>Retry later or choose another model; do not assume another model will answer the same way\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003C\u002Fdiv>\n\u003Cp>Some providers return their own error types inside the JSON body. The SDK’s \u003Ccode>APIStatusError\u003C\u002Fcode> exposes \u003Ccode>status_code\u003C\u002Fcode> and \u003Ccode>message\u003C\u002Fcode>, which is enough to log and decide.\u003C\u002Fp>\n\u003Ch2 id=\"switching-providers\">Switching providers\u003C\u002Fh2>\n\u003Cp>Only the environment changes. These are documented base URLs; the model ids are examples from our data:\u003C\u002Fp>\n\u003Cdiv class=\"table-wrap\" role=\"region\" aria-label=\"Scrollable table\" tabindex=\"0\">\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Where the model runs\u003C\u002Fth>\n\u003Cth>LLM_BASE_URL\u003C\u002Fth>\n\u003Cth>LLM_MODEL example\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>Featherless\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>https:\u002F\u002Fapi.featherless.ai\u002Fv1\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>huihui-ai\u002FLlama-3.3-70B-Instruct-abliterated\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Venice\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>https:\u002F\u002Fapi.venice.ai\u002Fapi\u002Fv1\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>venice-uncensored-1-2\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>MolochAI\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>https:\u002F\u002Fapi.molochai.dev\u002Fapi\u002Fv1\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>llama-3.3-70b-instruct-abliterated\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>llama.cpp \u003Ccode>llama-server\u003C\u002Fcode> on your machine\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>http:\u002F\u002Flocalhost:9931\u002Fv1\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>the name set with \u003Ccode>--alias\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003C\u002Fdiv>\n\u003Cp>The parameters each provider supports differ even when the endpoint is the same. Before relying on \u003Ccode>response_format\u003C\u002Fcode>, \u003Ccode>seed\u003C\u002Fcode> or \u003Ccode>stop\u003C\u002Fcode>, read the \u003Ca href=\"\u002Fguides\u002Fopenai-compatible-api\u002F\">OpenAI compatibility guide\u003C\u002Fa>, which lists what each provider documents as supported or ignored.\u003C\u002Fp>\n\u003Ch2 id=\"troubleshooting\">Troubleshooting\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>The reply is empty and \u003Ccode>finish_reason\u003C\u002Fcode> is \u003Ccode>length\u003C\u002Fcode>.\u003C\u002Fstrong> A thinking model used the whole \u003Ccode>max_tokens\u003C\u002Fcode> budget on reasoning. Raise \u003Ccode>max_tokens\u003C\u002Fcode>, or lower the reasoning effort if the model supports it.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>The answer contains \u003Ccode>&lt;think&gt;\u003C\u002Fcode> text.\u003C\u002Fstrong> Some servers return reasoning inside the content, others in a separate field. Check the model card and the provider’s docs for how reasoning is returned.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Requests hang.\u003C\u002Fstrong> Set \u003Ccode>timeout\u003C\u002Fcode> on the client, as in the first example, and stream long replies.\u003C\u002Fli>\n\u003C\u002Ful>\n",[17,21,24,27,30,33,36],{"id":18,"text":19,"level":20},"what-you-need","What you need",2,{"id":22,"text":23,"level":20},"keep-the-key-out-of-the-code","Keep the key out of the code",{"id":25,"text":26,"level":20},"first-request","First request",{"id":28,"text":29,"level":20},"streaming","Streaming",{"id":31,"text":32,"level":20},"errors-you-will-meet","Errors you will meet",{"id":34,"text":35,"level":20},"switching-providers","Switching providers",{"id":37,"text":38,"level":20},"troubleshooting","Troubleshooting",686,[41,45,49,53],{"title":42,"publisher":43,"url":44},"openai-python","GitHub (OpenAI)","https:\u002F\u002Fgithub.com\u002Fopenai\u002Fopenai-python",{"title":46,"publisher":47,"url":48},"Chat Completions API reference","OpenAI","https:\u002F\u002Fplatform.openai.com\u002Fdocs\u002Fapi-reference\u002Fchat",{"title":50,"publisher":51,"url":52},"llama.cpp HTTP server","GitHub (ggml-org)","https:\u002F\u002Fgithub.com\u002Fggml-org\u002Fllama.cpp\u002Fblob\u002Fmaster\u002Ftools\u002Fserver\u002FREADME.md",{"title":54,"publisher":55,"url":56},"OpenAI-Compatible Server","vLLM documentation","https:\u002F\u002Fdocs.vllm.ai\u002Fen\u002Flatest\u002Fserving\u002Fopenai_compatible_server.html",[58,61,64],{"q":59,"a":60},"Do I need a special SDK for an uncensored model?","No. Hosted providers of refusal-reduced models and local servers such as llama.cpp and vLLM expose the OpenAI chat-completions format, so the official openai package works with a different base URL.",{"q":62,"a":63},"Why does my request return 404?","Usually the base URL is missing a path segment (some providers use \u002Fapi\u002Fv1, others \u002Fv1) or the model id is not one the provider lists. Call client.models.list() to see the ids it accepts.",{"q":65,"a":66},"Can I set max_tokens above the provider's limit?","You can send it, but providers cap output per reply, and some replace your value with their cap. Check the provider's documented maximum output.",[68,72,76,80],{"path":69,"title":70,"description":71},"\u002Fguides\u002Fnodejs-uncensored-llm-api\u002F","Call an uncensored LLM API from Node.js","Call an uncensored or open-weight LLM from Node.js with the OpenAI SDK: environment variables, a first request, streaming with cancellation, errors and retries.",{"path":73,"title":74,"description":75},"\u002Fguides\u002Fstreaming-llm-responses\u002F","Streaming LLM responses with server-sent events","How LLM API streaming works: the server-sent events format, parsing chunks, the final usage chunk, cancelling a stream, timeouts and errors that arrive mid-stream.",{"path":77,"title":78,"description":79},"\u002Fguides\u002Ftool-calling-llm-api\u002F","Tool calling with an OpenAI-compatible LLM API","Tool calling with open-weight and uncensored LLMs: defining tools, the request and response loop, validating arguments, limits on rounds, and provider differences.",{"path":81,"title":82,"description":83},"\u002Fapi\u002F","Uncensored AI API options","Who serves which weights, and how compatible each API is.",{"summary":85,"ranOn":13,"environment":86,"liveEndpoint":87},"Every Python sample on this page was run unchanged against the mock OpenAI-compatible server in this site's repository (scripts\u002Fmock-openai-server.mjs), including a case where the server returns 429 twice and the SDK retries.","Python 3.12.4, openai 3.51.0 for Python, Windows 10",false,[89,81,90,73,91,92],"\u002Fapi\u002Fproviders\u002F","\u002Ftools\u002Ftoken-cost-calculator\u002F","\u002Ftools\u002Fcontext-window-planner\u002F","\u002Fguides\u002Fopenai-compatible-api\u002F",1794622336928]