Docs · OpenAI-compatible

Point the client at the base URL.

The API is OpenAI chat completions. Set the base URL, the key, and the model id. Leave the rest of the client alone.

Base URLhttps://api.norest.si/v1
Modelnorest-1
AuthAuthorization: Bearer nr_live_...

Every client on this page wants the URL ending in /v1. Do not append /chat/completions. The client adds that path itself. A key looks like nr_live_.... You get one when a slot opens. Until then the waitlist is the door. The strings below are placeholders.

curl

One call, no SDK. The body is a normal chat completion.

request.shshell
curl https://api.norest.si/v1/chat/completions \
  -H "Authorization: Bearer $NOREST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "norest-1",
    "messages": [
      {"role": "user", "content": "Write chapter 12."}
    ]
  }'

For tokens as they arrive, add "stream": true to the JSON and pass -N so curl prints each chunk instead of waiting for the whole reply.

Python

The official OpenAI SDK. pip install openai.

stream.pypython
from openai import OpenAI

client = OpenAI(
    base_url="https://api.norest.si/v1",
    api_key="nr_live_...",
)

stream = client.chat.completions.create(
    model="norest-1",
    messages=[{"role": "user", "content": "Write chapter 12."}],
    stream=True,
)

for chunk in stream:
    print(chunk.choices[0].delta.content or "", end="")

Drop stream=True and the reply is on response.choices[0].message.content.

Node

Same SDK, different field name. npm install openai. The option is baseURL, capital URL.

chat.mjsnode
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://api.norest.si/v1",
  apiKey: process.env.NOREST_API_KEY,
});

const stream = await client.chat.completions.create({
  model: "norest-1",
  messages: [{ role: "user", content: "Write chapter 12." }],
  stream: true,
});

for await (const chunk of stream) {
  process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}

Environment variables

The official OpenAI SDKs read the base URL and the key from the environment, and then you can omit both arguments. The model id is still an argument on the call. They do not read a model from the environment.

.envshell
export OPENAI_BASE_URL=https://api.norest.si/v1
export OPENAI_API_KEY=nr_live_...

Aider is the exception on this page. It wants the older name, OPENAI_API_BASE. That block is below.

Cursor

Cursor Settings, then Models. The shortcut is Cmd Shift J on macOS, Ctrl Shift J on Windows and Linux.

  1. Turn on OpenAI API Key and paste the key.
  2. Turn on Override OpenAI Base URL. Set it to https://api.norest.si/v1.
  3. Add a model named norest-1. The name has to match the id.
  4. Open a new chat and select norest-1.

While that override is on, Cursor also sends its own OpenAI-routed models to the same URL. Claude and Gemini keep their own key toggles and are left alone. Turn the override off when you want Cursor's models back.

Continue

Continue reads ~/.continue/config.yaml, and a .continue/config.yaml in the project if you want it local. Provider openai, field apiBase.

config.yamlcontinue
name: NoRest
version: 0.0.1
schema: v1
models:
  - name: norest-1
    provider: openai
    model: norest-1
    apiBase: https://api.norest.si/v1
    apiKey: nr_live_...
    roles:
      - chat
      - edit
      - apply

Do not give this model the autocomplete role. That role expects a completions endpoint, and this API is chat completions.

Cline and Roo Code

Open the Cline panel and click the gear. Set API Provider to OpenAI Compatible.

  1. Base URL: https://api.norest.si/v1
  2. API Key: the raw key, no Bearer prefix.
  3. Model ID: norest-1

Roo Code uses the same provider name and the same three fields. If the form asks for a context window or a price, those numbers only change how the extension trims the chat and what it prints as a cost. We don't meter tokens, and this page doesn't publish a context length. Set the trim wherever you want the conversation cut.

Aider

Aider's OpenAI-compatible client needs the openai/ prefix on the model, or it will not use this base URL.

shellaider
export OPENAI_API_BASE=https://api.norest.si/v1
export OPENAI_API_KEY=nr_live_...
aider --model openai/norest-1
.aider.conf.ymlyaml
openai-api-base: https://api.norest.si/v1
openai-api-key: nr_live_...
model: openai/norest-1

Aider will warn that it has no built-in card for norest-1. The calls still go to the base URL above. The warning is Aider saying it doesn't know the model, not a failed request.

OpenCode

Add a provider to ~/.config/opencode/opencode.json, or to opencode.json in the project. This is the config OpenCode documents for any OpenAI-compatible server.

opencode.jsonjson
{
  "$schema": "https://opencode.ai/config.json",
  "provider": {
    "norest": {
      "npm": "@ai-sdk/openai-compatible",
      "name": "NoRest",
      "options": {
        "baseURL": "https://api.norest.si/v1",
        "apiKey": "nr_live_..."
      },
      "models": {
        "norest-1": { "name": "norest-1" }
      }
    }
  }
}

Start OpenCode, run /models, and pick norest-1 under NoRest. To make it the default, set "model": "norest/norest-1" at the top level of the same file. Keep the key out of git. OpenCode will read {env:NOREST_API_KEY} in the apiKey field if you would rather not paste it.

Zed

Open the agent settings and choose Add Provider under LLM Providers. Name it NoRest. API URL https://api.norest.si/v1. Model id norest-1. Put the key in that provider's settings. Zed names an environment variable for the provider. Use that if you don't want the key in a file.

The same thing as JSON, in Zed settings:

settings.jsonzed
{
  "language_models": {
    "openai_compatible": {
      "norest": {
        "api_url": "https://api.norest.si/v1",
        "available_models": [
          {
            "name": "norest-1",
            "display_name": "norest-1",
            "max_tokens": 128000,
            "capabilities": {
              "tools": true,
              "images": true
            }
          }
        ]
      }
    }
  }
}

max_tokens here is Zed's trim budget, not a cap we enforce. 128000 is a starting value. Lower it if you want Zed to send less history. Leave the key out of this file.

What a request can contain

Messages use the roles system, user, and assistant. Tool calls use the OpenAI tools field, and the model answers with tool_calls on the assistant message. Send the tool result back as a message with role tool.

Image input is an image_url part on a user message, the same shape OpenAI uses. A URL or a data: URL both go in image_url.url.

messagejson
{
  "role": "user",
  "content": [
    {"type": "text", "text": "What is in this frame?"},
    {"type": "image_url", "image_url": {"url": "https://example.com/frame.jpg"}}
  ]
}

Limits and errors

Standard · $499 / month

60 / min · 4 in flight

Unlimited tokens, 24/7. A fifth request that is still running, or a 61st call in the minute, comes back 429.

Speed · $899 / month

120 / min · 8 in flight

Same model and the same API. Twice the rate limits, and output that streams about twice as fast.

A 429 carries Retry-After. Wait that many seconds and send the call again. Nothing is counted against the month, because there is no token meter. A 401 means the key is missing, wrong, or not on an open slot.

Prompts and outputs stay in GPU memory for the request. They are not stored, and they are not used for training.

The acceptable use policy still applies. Sexual content involving minors is banned. So is attacking systems you don't own, and targeting real people. Accounts that break it are closed.

Questions go to support@norest.si. A person reads them.

The machines are full.

Leave an email. You'll hear when a slot opens. One message, nothing else.