Making requests
The Nodea API is compatible with the OpenAI Chat Completions API. Point any OpenAI SDK, or any tool that lets you set an OpenAI base URL, at Nodea and use a Nodea API key.
| Base URL | https://api.getnodea.com/openai/v1 |
| Authentication | Authorization: Bearer <your API key> |
| Endpoints | POST /chat/completions, GET /models, GET /models/{model} |
Your organization comes from the API key, so every organization uses the same base URL.
Before you start
Section titled “Before you start”You need:
- an API key
- at least one enabled model
- at least one device serving it
The examples below read the key from the NODEA_API_KEY environment variable:
export NODEA_API_KEY="<your API key>"Chat completion
Section titled “Chat completion”Set model to a model name from your Models page.
curl https://api.getnodea.com/openai/v1/chat/completions \ -H "Authorization: Bearer $NODEA_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "bartowski/Llama-3.2-3B-Instruct-GGUF:Q4_K_M", "messages": [ {"role": "system", "content": "You are a concise assistant."}, {"role": "user", "content": "Explain what a GGUF file is in one sentence."} ], "temperature": 0.7, "max_tokens": 200 }'# pip install openaiimport osfrom openai import OpenAI
client = OpenAI( base_url="https://api.getnodea.com/openai/v1", api_key=os.environ["NODEA_API_KEY"],)
completion = client.chat.completions.create( model="bartowski/Llama-3.2-3B-Instruct-GGUF:Q4_K_M", messages=[ {"role": "system", "content": "You are a concise assistant."}, {"role": "user", "content": "Explain what a GGUF file is in one sentence."}, ], temperature=0.7, max_tokens=200,)print(completion.choices[0].message.content)// npm install openaiimport OpenAI from 'openai';
const client = new OpenAI({ baseURL: 'https://api.getnodea.com/openai/v1', apiKey: process.env.NODEA_API_KEY,});
const completion = await client.chat.completions.create({ model: 'bartowski/Llama-3.2-3B-Instruct-GGUF:Q4_K_M', messages: [ { role: 'system', content: 'You are a concise assistant.' }, { role: 'user', content: 'Explain what a GGUF file is in one sentence.' }, ], temperature: 0.7, max_tokens: 200,});console.log(completion.choices[0].message.content);The response has the same shape as OpenAI’s:
{ "id": "chatcmpl-6f1c2a9b8e7d4c3b2a1f0e9d", "object": "chat.completion", "created": 1760000000, "model": "bartowski/Llama-3.2-3B-Instruct-GGUF:Q4_K_M", "system_fingerprint": "fp_nodea_local", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "A GGUF file packages a model's weights and metadata in a single file for local inference.", "refusal": null }, "logprobs": null, "finish_reason": "stop" } ], "usage": { "prompt_tokens": 31, "completion_tokens": 19, "total_tokens": 50 }}Streaming
Section titled “Streaming”Set stream: true to receive the answer as it’s generated, as server-sent
events. Add stream_options: {"include_usage": true} to get token counts in a
final chunk.
curl -N https://api.getnodea.com/openai/v1/chat/completions \ -H "Authorization: Bearer $NODEA_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "bartowski/Llama-3.2-3B-Instruct-GGUF:Q4_K_M", "messages": [{"role": "user", "content": "Write a haiku about laptops."}], "stream": true }'stream = client.chat.completions.create( model="bartowski/Llama-3.2-3B-Instruct-GGUF:Q4_K_M", messages=[{"role": "user", "content": "Write a haiku about laptops."}], stream=True,)for chunk in stream: if chunk.choices and chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="", flush=True)const stream = await client.chat.completions.create({ model: 'bartowski/Llama-3.2-3B-Instruct-GGUF:Q4_K_M', messages: [{ role: 'user', content: 'Write a haiku about laptops.' }], stream: true,});for await (const chunk of stream) { process.stdout.write(chunk.choices[0]?.delta?.content ?? '');}The stream ends with data: [DONE]. If something goes wrong after streaming
has started, you get a final chunk with an error object instead, and no
[DONE]. See Streaming errors.
List available models
Section titled “List available models”GET /models returns the models enabled for your organization. A model can be
listed even when no device is serving it right now.
curl https://api.getnodea.com/openai/v1/models \ -H "Authorization: Bearer $NODEA_API_KEY"for model in client.models.list(): print(model.id)for await (const model of client.models.list()) { console.log(model.id);}{ "object": "list", "data": [ { "id": "bartowski/Llama-3.2-3B-Instruct-GGUF:Q4_K_M", "object": "model", "created": 1760000000, "owned_by": "ai4all" } ]}To fetch one model with GET /models/{model}, URL-encode the / in its name as
%2F.
Tool calling and structured output
Section titled “Tool calling and structured output”Nodea supports tools (function calling) and response_format (JSON mode and
JSON Schema), with some differences from OpenAI. Check
Chat completions and
Differences from OpenAI before relying on
them.
Handling errors
Section titled “Handling errors”Errors use OpenAI’s format, so the official SDKs raise their usual exceptions. The ones you’re most likely to see:
401: the key is wrong, deleted, or its creator has left the organization.403 model_disabled: an Admin has disabled or removed that model.503 model_unavailable: no device served the model within 120 seconds. Retry, or check the Fleet page.
See Errors for the full list.
Using other tools
Section titled “Using other tools”Any tool that accepts a custom OpenAI base URL can use Nodea. Set:
- base URL / API base to
https://api.getnodea.com/openai/v1 - API key to your Nodea key
- model to a model name from your Models page
Tools that need the Responses API, embeddings, images or audio won’t work. Nodea provides only Chat Completions and Models.