Nexforce Router
Uma API de inferência compatível com OpenAI. Um endpoint e uma key despacham para Anthropic, OpenAI, Google, DeepSeek, Moonshot e Workers AI, com fallback entre modelos e controle de custo por API key.
import { streamText } from 'ai';
import { createOpenAI } from '@ai-sdk/openai';
const nexforce = createOpenAI({
baseURL: 'https://router.nexforce.ai/v1',
apiKey: process.env.NEXFORCE_API_KEY,
});
const result = streamText({
model: nexforce('claude-3-5-sonnet'),
prompt: 'Por que o céu é azul?',
});
from openai import OpenAI
client = OpenAI(
base_url="https://router.nexforce.ai/v1",
api_key="nfc_...",
)
resp = client.chat.completions.create(
model="claude-3-5-sonnet",
messages=[{"role": "user", "content": "Por que o céu é azul?"}],
)
print(resp.choices[0].message.content)
curl https://router.nexforce.ai/v1/chat/completions \
-H "Authorization: Bearer nfc_..." \
-H "Content-Type: application/json" \
-d '{
"model": "claude-3-5-sonnet",
"messages": [
{ "role": "user", "content": "Por que o céu é azul?" }
]
}'
Se a sua ferramenta já fala com a API da OpenAI, ela fala com o Nexforce Router. Troque a base URL e a API key, e mantenha o resto do código.
Compatível com o formato /v1/chat/completions da OpenAI, incluindo streaming, tools (function calling) e entrada multimodal.
Início rápido
1. Crie uma API key no Console (formato nfc_...). 2. Aponte o cliente para a base URL. 3. Chame qualquer modelo do catálogo.
curl https://router.nexforce.ai/v1/chat/completions \
-H "Authorization: Bearer $NEXFORCE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-3-5-sonnet",
"messages": [{ "role": "user", "content": "Olá!" }]
}'
from openai import OpenAI
client = OpenAI(
base_url="https://router.nexforce.ai/v1",
api_key="nfc_...",
)
resp = client.chat.completions.create(
model="claude-3-5-sonnet",
messages=[{"role": "user", "content": "Olá!"}],
)
print(resp.choices[0].message.content)
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://router.nexforce.ai/v1",
apiKey: process.env.NEXFORCE_API_KEY,
});
const resp = await client.chat.completions.create({
model: "claude-3-5-sonnet",
messages: [{ role: "user", content: "Olá!" }],
});
Autenticação
Toda request autenticada usa um Bearer token no header Authorization. A key tem o prefixo nfc_ e carrega as regras de acesso (modelos permitidos, limite de custo, rate limit).
Authorization: Bearer nfc_suaorg_xxxxxxxxxxxxxxxx
A listagem de modelos (GET /v1/models) é pública e não exige key. Com uma key válida, a lista é filtrada para os modelos que aquela key pode chamar.
Modelos
Catálogo carregado ao vivo de GET /v1/models. É essa resposta que as ferramentas de código leem para popular o seletor de modelos. O preço de cada modelo fica no Console, não é exposto aqui.
| ID | Provedor | Tools | Visão |
|---|---|---|---|
| Carregando catálogo… | |||
Chat completions
O corpo segue o schema da OpenAI. O roteamento para o provedor certo é automático a partir do campo model. A resposta inclui headers X-Nexforce-Requested-Model e X-Nexforce-Served-Model para você sempre saber qual modelo respondeu.
Fallback por modelo
Quando o modelo pedido falha (sem credencial, indisponível ou erro 5xx), o router tenta os próximos modelos da cadeia configurada na key, em ordem, respeitando o allow/deny daquela key.
Parâmetros suportados
model— obrigatório. Id do catálogo (ex:claude-3-5-sonnet).messages— obrigatório. Array no formato OpenAI (system,user,assistant,tool).stream—truepara Server-Sent Events.temperature,top_p,max_tokens,stop,frequency_penalty,presence_penalty.tools,tool_choice— function calling (nos modelos com suporte a tools).
Streaming
Defina "stream": true para receber a resposta em chunks no formato chat.completion.chunk, idêntico à OpenAI. O encerramento é sinalizado por data: [DONE].
stream = client.chat.completions.create(
model="claude-3-5-sonnet",
messages=[{"role": "user", "content": "Conte uma história"}],
stream=True,
)
for chunk in stream:
print(chunk.choices[0].delta.content or "", end="")
Erros
Códigos HTTP padrão, com corpo JSON no formato de erro da OpenAI.
| Código | Significado |
|---|---|
| 401 | Key ausente ou inválida |
| 402 | Saldo insuficiente — recarregue a wallet |
| 403 | Modelo/provedor bloqueado para esta key, ou conta suspensa |
| 404 | Modelo fora do catálogo |
| 429 | Rate limit ou orçamento excedido |
| 5xx | Falha do provedor (dispara fallback quando configurado) |
O corpo segue o formato de erro da OpenAI, então o tratamento dos SDKs funciona sem mudança:
from openai import APIStatusError
try:
resp = client.chat.completions.create(model="claude-3-5-sonnet", messages=messages)
except APIStatusError as e:
if e.status_code == 402:
print("Saldo insuficiente — recarregue a wallet.")
elif e.status_code == 429:
print("Rate limit ou orçamento excedido.")
else:
print(e.status_code, e.response.text)
Mensagem de sistema
Use uma mensagem system no início do array para definir o comportamento do modelo. As demais mensagens seguem o padrão user / assistant.
curl https://router.nexforce.ai/v1/chat/completions \
-H "Authorization: Bearer $NEXFORCE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-3-5-sonnet",
"messages": [
{ "role": "system", "content": "Você é um assistente fiscal brasileiro. Responda em pt-BR, de forma objetiva." },
{ "role": "user", "content": "O que é IRRF sobre software importado?" }
]
}'
Conversa multi-turno
Mantenha o histórico enviando as mensagens anteriores (user e assistant) a cada chamada. O router não guarda estado: o contexto é o array que você manda.
messages = [
{"role": "system", "content": "Você é um tutor de matemática."},
{"role": "user", "content": "Quanto é 7 x 8?"},
{"role": "assistant", "content": "56."},
{"role": "user", "content": "E dividido por 4?"},
]
resp = client.chat.completions.create(model="claude-3-5-sonnet", messages=messages)
messages.append({"role": "assistant", "content": resp.choices[0].message.content})
Ajustando a geração
Os parâmetros de amostragem da OpenAI são repassados ao provedor. max_tokens é aceito para todos os modelos (para a família o-series da OpenAI o router o converte para max_completion_tokens automaticamente).
{
"model": "claude-3-5-sonnet",
"messages": [{ "role": "user", "content": "Liste 3 nomes para uma cafeteria." }],
"temperature": 0.7,
"top_p": 0.9,
"max_tokens": 200,
"stop": ["\n\n"],
"frequency_penalty": 0.3,
"presence_penalty": 0.0
}
Function calling (tools)
Defina as funções em tools no formato da OpenAI. Quando o modelo decide chamar uma função, a resposta vem com finish_reason: "tool_calls" e os tool_calls na mensagem do assistente. Você executa a função e devolve o resultado numa mensagem role: "tool".
1. Request com a tool definida
{
"model": "claude-3-5-sonnet",
"messages": [{ "role": "user", "content": "Qual a previsão do tempo em São Paulo?" }],
"tools": [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Retorna o clima atual de uma cidade",
"parameters": {
"type": "object",
"properties": { "city": { "type": "string" } },
"required": ["city"]
}
}
}],
"tool_choice": "auto"
}
tool_choice aceita "auto" (padrão), "none", "required" ou { "type": "function", "function": { "name": "..." } } para forçar uma função específica.
2. Resposta do modelo (pede a função)
{
"choices": [{
"finish_reason": "tool_calls",
"message": {
"role": "assistant",
"tool_calls": [{
"id": "call_abc123",
"type": "function",
"function": { "name": "get_weather", "arguments": "{\"city\":\"São Paulo\"}" }
}]
}
}]
}
3. Devolva o resultado da função
{
"model": "claude-3-5-sonnet",
"messages": [
{ "role": "user", "content": "Qual a previsão do tempo em São Paulo?" },
{ "role": "assistant", "tool_calls": [{ "id": "call_abc123", "type": "function", "function": { "name": "get_weather", "arguments": "{\"city\":\"São Paulo\"}" } }] },
{ "role": "tool", "tool_call_id": "call_abc123", "content": "28°C, ensolarado" }
]
}
Visão (imagem)
Mande imagens no array de conteúdo com blocos image_url, no formato multimodal da OpenAI. Use um modelo com suporte a visão (a coluna "Visão" na seção Modelos indica quais).
Por URL
{
"model": "claude-3-5-sonnet",
"messages": [{
"role": "user",
"content": [
{ "type": "text", "text": "O que tem nesta imagem?" },
{ "type": "image_url", "image_url": { "url": "https://exemplo.com/foto.jpg" } }
]
}]
}
Por base64 (data URI)
{ "type": "image_url", "image_url": { "url": "data:image/jpeg;base64,/9j/4AAQSkZJRg..." } }
Documentos (PDF)
Envie um PDF como bloco file com o conteúdo em base64 em file_data. O media_type assume application/pdf quando omitido. Suportado em modelos com visão/documentos (ex: Claude).
{
"model": "claude-3-5-sonnet",
"messages": [{
"role": "user",
"content": [
{ "type": "text", "text": "Resuma este contrato." },
{ "type": "file", "file": {
"filename": "contrato.pdf",
"media_type": "application/pdf",
"file_data": "JVBERi0xLjcKJ..."
}}
]
}]
}
Modelos open-source (Workers AI)
Os modelos com provedor workers-ai rodam direto na Cloudflare, sem precisar de chave de provedor. Use o id do catálogo normalmente; o roteamento é automático.
curl https://router.nexforce.ai/v1/chat/completions \
-H "Authorization: Bearer $NEXFORCE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "llama-3.3-70b",
"messages": [{ "role": "user", "content": "Escreva um haicai sobre código." }]
}'
Atribuição de agente
Marque cada request com o nome do agente que a originou. O router registra esse rótulo no uso, então você consegue separar custo e volume por agente no Console. O nome é truncado em 120 caracteres.
Forma direta
curl https://router.nexforce.ai/v1/chat/completions \
-H "Authorization: Bearer $NEXFORCE_API_KEY" \
-H "Content-Type: application/json" \
-H "X-Nexforce-Agent: suporte-bot" \
-d '{ "model": "claude-3-5-sonnet", "messages": [{ "role": "user", "content": "Olá!" }] }'
Nos SDKs da OpenAI, mande pelo extra_headers (Python) ou defaultHeaders (TypeScript):
client.chat.completions.create(
model="claude-3-5-sonnet",
messages=[{"role": "user", "content": "Olá!"}],
extra_headers={"X-Nexforce-Agent": "suporte-bot"},
)
Via metadata (JSON)
Alternativa quando você já manda outros metadados. O router lê o campo agent (também aceita agent_label e _agent):
X-Nexforce-Metadata: {"agent": "suporte-bot"}
Se os dois headers vierem juntos, o X-Nexforce-Agent tem precedência sobre o agent do metadata.
Smart Routing
Quando ativado na sua configuração (no Console), o router pode trocar o modelo pedido por um equivalente mais barato antes do dispatch, preservando as capacidades necessárias (tools, visão). A troca nunca é escondida: o modelo que respondeu vem sempre nos headers de resposta, e a economia é registrada no uso.
Controle por request
Dois headers deixam você desligar ou fixar o modelo numa request específica, sobrepondo a configuração:
# Desliga o Smart Routing só nesta request (aceita: off, false, 0)
X-Nexforce-Smart-Routing: off
# Ou força exatamente o modelo pedido, sem substituição
X-Nexforce-Pin-Model: true
Esses headers só têm efeito quando o Smart Routing está habilitado na configuração. Com ele desligado, o modelo pedido é sempre o servido.
Modelo nexforce/smart-route
Em vez de escolher um modelo, aponte a request para o id nexforce/smart-route e deixe a Nexforce escolher por você. O router resolve esse id para um modelo de referência (a âncora) e, quando existe um equivalente mais barato com as mesmas capacidades (tools, visão), serve o equivalente. Quando não existe, serve a própria âncora. O modelo que de fato respondeu vem sempre nos headers de resposta.
curl https://router.nexforce.ai/v1/chat/completions \
-H "Authorization: Bearer $NEXFORCE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "nexforce/smart-route",
"messages": [{ "role": "user", "content": "Resuma este artigo em 3 tópicos." }]
}'
É diferente do Smart Routing por configuração, que age sobre o modelo que você nomeia. O nexforce/smart-route é a própria escolha: liga o roteamento nesta request, sem precisar habilitar nada na key. O id também aparece no catálogo (/v1/models), pronto para uso em qualquer ferramenta compatível com OpenAI.
A resposta traz X-Nexforce-Smart-Route: true junto com o X-Nexforce-Served-Model, deixando explícito que a Nexforce escolheu o modelo por você.
Headers de resposta
Toda resposta de chat completion traz headers que dizem qual modelo realmente respondeu e quão fresca está a configuração usada.
| Header | Descrição |
|---|---|
| X-Nexforce-Requested-Model | O modelo que você pediu |
| X-Nexforce-Served-Model | O modelo que de fato respondeu |
| X-Nexforce-Substituted | true quando o Smart Routing trocou o modelo (ausente caso contrário) |
| X-Nexforce-Smart-Route | true quando a request usou o modelo nexforce/smart-route e a Nexforce escolheu o modelo servido (ausente caso contrário) |
| X-Config-Synced-At | Timestamp do snapshot de configuração usado na request |
HTTP/2 200
X-Nexforce-Requested-Model: claude-3-5-sonnet
X-Nexforce-Served-Model: claude-3-5-haiku
X-Nexforce-Substituted: true
X-Config-Synced-At: 2026-06-18T11:42:07.000Z
Cursor
Em Settings → Models → OpenAI API Key, ative "Override OpenAI Base URL", cole https://router.nexforce.ai/v1 e a sua key nfc_.... O Cursor não lista modelos sozinho: adicione os ids manualmente (ex: claude-3-5-sonnet).
Cline / Roo Code
Selecione o provider OpenAI Compatible. Base URL https://router.nexforce.ai/v1, API key nfc_.... O seletor de modelos popula automaticamente a partir de /v1/models.
Continue
models:
- name: Nexforce Sonnet
provider: openai
model: claude-3-5-sonnet
apiBase: https://router.nexforce.ai/v1
apiKey: nfc_...
OpenCode
{
"provider": {
"nexforce": {
"npm": "@ai-sdk/openai-compatible",
"options": {
"baseURL": "https://router.nexforce.ai/v1",
"apiKey": "nfc_..."
}
}
}
}