Nexforce Nexforce Router · docs Console Criar API key

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.

OpenAIAnthropicGoogleDeepSeekMoonshotWorkers AI e muitos mais
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.

BASE https://router.nexforce.ai/v1

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
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á!" }]
  }'
python
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)
typescript
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).

http
Authorization: Bearer nfc_suaorg_xxxxxxxxxxxxxxxx
i

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.

GET https://router.nexforce.ai/v1/models
carregando…
IDProvedorTipoToolsVisão
Carregando catálogo…

Chat completions

POST https://router.nexforce.ai/v1/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.

Embeddings

POST https://router.nexforce.ai/v1/embeddings

Vetores para RAG e busca semântica, no schema da OpenAI e com a mesma key nfc_. Modelos disponíveis: text-embedding-3-small, text-embedding-3-large, text-embedding-ada-002 (OpenAI) e gemini-embedding-001, gemini-embedding-2 (Google).

python
from openai import OpenAI

client = OpenAI(base_url="https://router.nexforce.ai/v1", api_key="nfc_...")

resp = client.embeddings.create(
    model="text-embedding-3-small",
    input=["primeiro documento", "segundo documento"],
)
vetores = [d.embedding for d in resp.data]
bash
curl https://router.nexforce.ai/v1/embeddings \
  -H "Authorization: Bearer $NEXFORCE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "text-embedding-3-small",
    "input": ["primeiro documento", "segundo documento"]
  }'

O modelo nunca é trocado

Smart Routing não vale para embeddings. Vetores de dois modelos diferentes são incomparáveis: trocar o modelo por baixo dos panos corromperia o índice que você já gravou. O que você pede é o que roda, e o header X-Nexforce-Served-Model devolve sempre o mesmo id.

Parâmetros

  • model — obrigatório. Id de um modelo de embedding. Um modelo de chat aqui devolve 400 com o código wrong_endpoint e o endpoint certo.
  • input — obrigatório. Um texto, uma lista de até 2048 textos, ou token ids.
  • dimensions, encoding_format (float ou base64), user — repassados ao provedor sem alteração.
  • Sem stream: a resposta vem inteira de uma vez.

A cobrança conta só tokens de entrada: embedding não tem saída em token nem camada de cache.

Geração de imagem

POST https://router.nexforce.ai/v1/chat/completions

É o mesmo endpoint do chat, com a mesma key nfc_: aqui gerar imagem é uma capacidade do modelo de chat, não uma API separada. Mande "modalities": ["image", "text"] e as imagens voltam em choices[].message.images. Modelos: google/gemini-3-pro-image, google/gemini-3.1-flash-image, google/gemini-3.1-flash-lite-image, google/gemini-2.5-flash-image. A lista corrente é a do /v1/models: serve qualquer modelo cujo architecture.output_modalities inclua image.

python
import base64
from openai import OpenAI

client = OpenAI(base_url="https://router.nexforce.ai/v1", api_key="nfc_...")

resp = client.chat.completions.create(
    model="google/gemini-3-pro-image",
    modalities=["image", "text"],
    messages=[{"role": "user", "content": "um gato de capacete de obra, luz de estúdio"}],
)

url = resp.choices[0].message.images[0]["image_url"]["url"]
cabecalho, dados = url.split(",", 1)      # cabecalho: "data:image/png;base64"
ext = cabecalho.split(";")[0].split("/")[-1]  # leia, nao assuma png
open(f"gato.{ext}", "wb").write(base64.b64decode(dados))
bash
curl https://router.nexforce.ai/v1/chat/completions \
  -H "Authorization: Bearer $NEXFORCE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "google/gemini-3-pro-image",
    "modalities": ["image", "text"],
    "messages": [{"role": "user", "content": "um gato de capacete de obra"}]
  }'

O que volta

  • Cada imagem é um data URI (data:<mime>;base64,...) dentro de {"type": "image_url", "image_url": {"url": "..."}}. O Router não hospeda arquivo, então não há URL para baixar depois e nada expira.
  • O formato varia por modelo, então leia o mime do próprio data URI em vez de assumir PNG. Medido: gemini-2.5-flash-image devolve PNG em 1024x1024 e gemini-3-pro-image devolve JPEG em 1408x768. Nem o formato nem a proporção são parâmetro que você escolhe.
  • message.content traz o texto que o modelo escreveu junto com a imagem. É string vazia quando ele devolveu só imagem, nunca null.
  • stream: true funciona e entrega um chunk com delta.images, seguido de data: [DONE]. O provedor produz a imagem inteira de uma vez, então não há o que entregar em partes.

Editar imagem

Mesma chamada: mande a imagem de origem como bloco image_url (data URI) junto com a instrução do que mudar.

O modelo nunca é trocado

Mesma regra do embedding, pela mesma razão: imagem não é comparável a texto, então Smart Routing não vale para um request que gera imagem.

Cobrança

Por token, como em texto. A imagem gerada tem custo em token que o provedor reporta (cerca de 1290 tokens por imagem no gemini-2.5-flash-image) e é cobrada na tarifa de saída do modelo. Não existe tarifa por imagem.

O que não é servido

Modelo de imagem pura, sem texto na saída (gpt-image-1, gpt-image-2): não tem turno de conversa e exige a Images API do próprio provedor. Devolve 400 com o código unsupported_capability.

Parâmetros suportados

Do /v1/chat/completions. Os de embeddings estão na seção acima.

  • model — obrigatório. Id do catálogo (ex: claude-3-5-sonnet).
  • messages — obrigatório. Array no formato OpenAI (system, user, assistant, tool).
  • streamtrue para Server-Sent Events.
  • temperature, top_p, max_tokens, stop, frequency_penalty, presence_penalty.
  • tools, tool_choice — function calling (nos modelos com suporte a tools).
  • modalities: ["image", "text"] para pedir imagem na resposta (ver Geração de imagem).

Streaming

Defina "stream": true para receber a resposta em chunks no formato chat.completion.chunk, idêntico à OpenAI. O encerramento é sinalizado por data: [DONE].

python
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ódigoSignificado
401Key ausente ou inválida
402Saldo insuficiente — recarregue a wallet
403Modelo/provedor bloqueado para esta key, ou conta suspensa
404Modelo fora do catálogo
400Código wrong_endpoint: key certa, endpoint errado para o modelo (chat vs embeddings)
400Código unsupported_capability: o modelo não faz o que o request pede (modelo de imagem pura no chat, imagem de entrada em modelo só de texto, saída de áudio ou vídeo)
429Rate limit ou orçamento excedido
5xxFalha 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:

python
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
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.

python
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).

json
{
  "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

json
{
  "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)

json
{
  "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

json
{
  "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

json
{
  "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)

json
{ "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).

json
{
  "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
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
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):

python
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):

http
X-Nexforce-Metadata: {"agent": "suporte-bot"}
i

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:

http
# 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
i

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
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.

i

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.

HeaderDescrição
X-Nexforce-Requested-ModelO modelo que você pediu
X-Nexforce-Served-ModelO modelo que de fato respondeu
X-Nexforce-Substitutedtrue quando o Smart Routing trocou o modelo (ausente caso contrário)
X-Nexforce-Smart-Routetrue quando a request usou o modelo nexforce/smart-route e a Nexforce escolheu o modelo servido (ausente caso contrário)
X-Config-Synced-AtTimestamp do snapshot de configuração usado na request
curl -i
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

config.yaml
models:
  - name: Nexforce Sonnet
    provider: openai
    model: claude-3-5-sonnet
    apiBase: https://router.nexforce.ai/v1
    apiKey: nfc_...

OpenCode

opencode.json
{
  "provider": {
    "nexforce": {
      "npm": "@ai-sdk/openai-compatible",
      "options": {
        "baseURL": "https://router.nexforce.ai/v1",
        "apiKey": "nfc_..."
      }
    }
  }
}