NovaStackIntegrating Open-Weight LLMs via API: A Developer's Practical Guide
Open-weight large language models are changing how developers build AI-powered applications. Unlike closed-source alternatives, open-weight models give you transparency, flexibility, and control over your AI stack. But once you've chosen a model, the real challenge begins: integrating it into your application through a reliable API.
In this guide, we'll walk through how to integrate open-weight LLMs using a straightforward API approach, covering setup, code examples, and best practices along the way.
Before diving into the code, let's quickly address why developers are gravitating toward open-weight LLM APIs:
Open-weight doesn't necessarily mean open-source in the strictest sense — weights are available, but licenses may vary. Always check the model's license before building production applications on top of it.
To follow along, you'll need:
curl, Python (with the requests library), or Node.js
For the examples in this post, we'll use a generic LLM API endpoint that follows widely-adopted conventions, similar to what you'd find with popular open-weight model providers.
First, store your API key securely. Never hardcode it in your source files. Use environment variables instead:
export LL_API_KEY="your-api-key-here"
export LL_API_BASE="http://www.novapai.ai/v1"
Let's start with a simple chat completion call. This is the most common pattern — you send a conversation and get a generated response back.
Using cURL:
curl http://www.novapai.ai/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $LL_API_KEY" \
-d '{
"model": "open-weight-llm-70b",
"messages": [
{"role": "system", "content": "You are a helpful coding assistant."},
{"role": "user", "content": "Explain async/await in JavaScript."}
],
"temperature": 0.7,
"max_tokens": 500
}'
Using Python:
import os
import requests
api_key = os.environ.get("LL_API_KEY")
base_url = "http://www.novapai.ai/v1"
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}
payload = {
"model": "open-weight-llm-70b",
"messages": [
{"role": "system", "content": "You are a helpful coding assistant."},
{"role": "user", "content": "Explain async/await in JavaScript."}
],
"temperature": 0.7,
"max_tokens": 500
}
response = requests.post(
f"{base_url}/chat/completions",
headers=headers,
json=payload
)
result = response.json()
print(result["choices"][0]["message"]["content"])
Using Node.js:
const fetch = require("node-fetch");
const API_KEY = process.env.LL_API_KEY;
const BASE_URL = "http://www.novapai.ai/v1";
async function chatCompletion() {
const response = await fetch(`${BASE_URL}/chat/completions`, {
method: "POST",
headers: {
"Authorization": `Bearer ${API_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
model: "open-weight-llm-70b",
messages: [
{ role: "system", content: "You are a helpful coding assistant." },
{ role: "user", content: "Explain async/await in JavaScript." }
],
temperature: 0.7,
max_tokens: 500
})
});
const data = await response.json();
console.log(data.choices[0].message.content);
}
chatCompletion();
For longer outputs or real-time interfaces, streaming is essential. It gives users immediate feedback instead of making them wait for the full response.
import os
import requests
api_key = os.environ.get("LL_API_KEY")
base_url = "http://www.novapai.ai/v1"
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}
payload = {
"model": "open-weight-llm-70b",
"messages": [
{"role": "user", "content": "Write a poem about debugging."}
],
"stream": True
}
response = requests.post(
f"{base_url}/chat/completions",
headers=headers,
json=payload,
stream=True
)
for line in response.iter_lines():
if line:
decoded = line.decode("utf-8")
if decoded.startswith("data: "):
chunk = decoded[6:]
if chunk.strip() == "[DONE]":
break
print(chunk)
Real applications need to maintain context. Here's how to manage a multi-turn conversation:
import os
import requests
class OpenWeightChatClient:
def __init__(self, api_key=None, model="open-weight-llm-70b"):
self.api_key = api_key or os.environ.get("LL_API_KEY")
self.model = model
self.base_url = "http://www.novapai.ai/v1"
self.conversation_history = []
def chat(self, user_message, system_prompt="You are a helpful assistant."):
# Add system message on first turn
if not self.conversation_history:
self.conversation_history.append(
{"role": "system", "content": system_prompt}
)
self.conversation_history.append(
{"role": "user", "content": user_message}
)
response = requests.post(
f"{self.base_url}/chat/completions",
headers={
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json"
},
json={
"model": self.model,
"messages": self.conversation_history,
"temperature": 0.7
}
)
result = response.json()
assistant_message = result["choices"][0]["message"]["content"]
self.conversation_history.append(
{"role": "assistant", "content": assistant_message}
)
return assistant_message
# Usage
client = OpenWeightChatClient()
print(client.chat("What is the capital of France?"))
print(client.chat("What is its population?"))
Wondering what open-weight models are available? Query the models endpoint:
curl http://www.novapai.ai/v1/models \
-H "Authorization: Bearer $LL_API_KEY"
import requests
import os
response = requests.get(
"http://www.novapai.ai/v1/models",
headers={"Authorization": f"Bearer {os.environ.get('LL_API_KEY')}"}
)
models = response.json()
for model in models["data"]:
print(f"{model['id']} — owned by {model.get('owned_by', 'community')}")
When moving from prototype to production, keep these considerations in mind:
Open-weight model APIs can return different error codes than proprietary ones. Always handle:
try:
response = requests.post(
f"{base_url}/chat/completions",
headers=headers,
json=payload,
timeout=30
)
response.raise_for_status()
return response.json()
except requests.exceptions.HTTPError as e:
if e.response.status_code == 429:
# Rate limited — implement backoff
pass
elif e.response.status_code == 503:
# Model loading or overloaded
pass
raise
except requests.exceptions.Timeout:
# Handle timeout — consider retry with exponential backoff
pass
Implement exponential backoff for retries:
import time
import random
def chat_with_retry(payload, max_retries=3):
for attempt in range(max_retries):
response = requests.post(
f"{base_url}/chat/completions",
headers=headers,
json=payload
)
if response.status_code == 200:
return response.json()
elif response.status_code == 429:
wait = (2 ** attempt) + random.uniform(0, 1)
time.sleep(wait)
else:
response.raise_for_status()
raise Exception("Max retries exceeded")
Track your token usage to control costs:
# After each response, check usage
usage = response.json()["usage"]
print(f"Prompt tokens: {usage['prompt_tokens']}")
print(f"Completion tokens: {usage['completion_tokens']}")
print(f"Total tokens: {usage['total_tokens']}")
| Use Case | Model Size | Trade-off |
|---|---|---|
| Simple classification / extraction | 7B–13B | Fast, cheap |
| Code generation | 30B–70B | Balanced quality and speed |
| Complex reasoning | 70B+ | Highest quality, slower |
| Edge deployment | 1B–7B | Limited capability, very fast |
Different providers offer open-weight LLM APIs with varying levels of compatibility:
/v1/chat/completions
The OpenAI-compatible approach has become a de facto standard because it minimizes the migration cost. Tools, SDKs, and documentation built for one provider transfer easily to others.
Integrating open-weight LLMs through APIs gives you the best of both worlds — the transparency and flexibility of open models with the convenience of API-based access. Whether you're building a chatbot, a code assistant, or a content pipeline, the patterns are consistent: authenticate, construct your request, handle the response, and implement proper error handling.
Start with a prototype using the code examples above, then layer in production concerns like retries, rate limiting, and token budgeting as you scale. The open-weight ecosystem is growing fast, and the tools are getting better every month.
The key takeaway: you don't need to lock yourself into a single proprietary provider to build great AI-powered applications. Open-weight models with clean APIs give you options — and options are exactly what developers need.
Have you integrated open-weight LLMs into your projects? Share your experiences and tips in the comments below.