Migrar de OpenAI ou OpenRouter
Adapte seu código com as bibliotecas OpenAI à SpicyAPI: URL base, chave, ID do modelo, diferenças nos campos e ajustes para quem usa OpenRouter.
Se seu código já chama um modelo de chat pelas bibliotecas oficiais da OpenAI ou por uma ferramenta com configuração “OpenAI-compatible”, migrar para a SpicyAPI exige três mudanças: URL base, chave de API e ID do modelo. Esta página lista essas mudanças e depois as diferenças de comportamento, para que a migração não termine com um 400 inesperado. Para programas completos de exemplo, veja Exemplos de chat.
As três mudanças
| Configuração | Valor na SpicyAPI |
|---|---|
| URL base | https://api.spicyapi.ai/v1 |
| Chave de API | Uma chave da SpicyAPI, que começa com sk-spicy-, enviada como Authorization: Bearer |
| Modelo | ID exato de um modelo de texto do catálogo em tempo real, por exemplo em SPICY_MODEL; veja IDs de modelos |
import os
from openai import OpenAI
# Before: client = OpenAI() (reads OPENAI_API_KEY, calls api.openai.com)
client = OpenAI(api_key=os.environ["SPICY_API_KEY"], base_url="https://api.spicyapi.ai/v1")
reply = client.chat.completions.create(
model=os.environ["SPICY_MODEL"],
messages=[{"role": "user", "content": "Say hello in one sentence."}],
)
print(reply.choices[0].message.content)As bibliotecas OpenAI também leem OPENAI_BASE_URL e OPENAI_API_KEY do ambiente. Definir as duas permite executar o código existente sem alterações, mas também redireciona todas as outras chamadas OpenAI no mesmo processo; prefira informar os valores explicitamente, como acima.
IDs de modelos
IDs de modelos de texto têm o formato publisher/model/chat. Copie-os do catálogo em tempo real, em vez de adaptar um nome conhecido de outro serviço:
GET /api/v1/models?modality=textlista somente modelos de texto com seus preços e inclui oinputSchemade cada modelo comincludeSchema=1. Veja Catálogo de modelos e schemas.GET /v1/modelsé a lista no formato OpenAI consultada pelas ferramentas. Sem filtro, inclui modelos de imagem, vídeo e áudio, que não podem ser chamados em/v1/chat/completions; adicione?modality=textpara obter apenas modelos de texto. Além dos quatro campos OpenAI, cada item temmodalityetask; modelos de chat também têmcontext_length,max_output_tokensesupported_parameters, os campos de solicitação aceitos pelo modelo. As bibliotecas OpenAI ignoram os campos extras.
Um ID inexistente retorna 404 com code: "model_not_found" e param: "model"; a mensagem mostra o formato do ID e, quando existe, o modelo de chat disponível mais próximo. Chamar um modelo de imagem ou vídeo em um endpoint de chat retorna 400, informando que ele não é um modelo de chat. IDs de outros serviços podem parecer semelhantes, mas não são intercambiáveis: os nomes dos criadores têm grafias diferentes, sufixos como :free não existem aqui, e modelos de texto terminam em /chat.
Campos da solicitação
Cada modelo publica os campos aceitos em seu inputSchema e, por nome, em supported_parameters de /v1/models.
- Campos de amostragem não listados pelo modelo são ignorados.
temperature,top_p,top_k,min_p,top_a,frequency_penalty,presence_penaltyerepetition_penaltysó alteram a aleatoriedade ou a repetição da resposta. Como muitos clientes os enviam por padrão, um modelo que não os aceita simplesmente gera sem eles. O mesmo vale paran: 1,stopvazio,stopem um modelo sem sequências de parada ereasoning_effortem um modelo sem níveis de raciocínio. A resposta informa o que foi ignorado no cabeçalhoX-Spicy-Ignored-Params, por exemplofrequency_penalty, presence_penalty. - Valores fora do que o modelo aceita são ajustados para o valor permitido mais próximo. Um valor de amostragem fora do intervalo é limitado à fronteira; uma lista
stopmaior que o permitido mantém suas primeiras entradas; um nívelreasoning_effortnão oferecido vira o nível disponível mais próximo, com preferência pelo menor em caso de empate. A resposta lista cada mudança comoname=valueno cabeçalhoX-Spicy-Adjusted-Params, por exemploreasoning_effort=low, temperature=1. - Qualquer outro campo não listado pelo modelo é recusado com
400,code: "unsupported_parameter",paramdefinido como o campo e uma mensagem que o identifica, comologprobs: is not a supported field. Esses campos alteram o que você recebe; assim, você nunca paga por uma resposta gerada sem um recurso que acreditava ter enviado.
| Campo | Na SpicyAPI |
|---|---|
messages | Mesmo formato. Papéis system, developer, user, assistant e tool. Conteúdo textual funciona em todos; partes image_url somente em modelos cujo schema as lista |
max_tokens, max_completion_tokens | Ambos aceitos, com o mesmo significado. Tokens de raciocínio contam para esse limite. Cada modelo tem seu próprio valor padrão e máximo |
stream, stream_options | Aceitos, com as regras de uso da OpenAI: um stream só inclui uso quando include_usage é true; veja Respostas e streams |
tools, tool_choice | Aceitos em todos os modelos de texto. Alguns aceitam somente auto e none em tool_choice; nesses casos, forçar uma ferramenta pelo nome retorna 400 |
parallel_tool_calls | Somente em modelos que listam o campo |
reasoning_effort | Na maioria dos modelos, mas os valores permitidos variam: alguns acrescentam none, minimal, xhigh ou max, e alguns não têm medium. Um valor não oferecido é ajustado para o mais próximo disponível; consulte o enum se o nível exato importar |
temperature, top_p, top_k, frequency_penalty, presence_penalty | Usados em modelos que os listam, limitados ao intervalo do modelo e ignorados nos demais, com indicação em X-Spicy-Ignored-Params. Vários modelos não aceitam nenhum campo de amostragem |
seed | Somente em modelos que listam o campo |
stop | Usado em modelos que o listam, reduzido à quantidade máxima do modelo; ignorado em modelos sem sequências de parada e quando vazio |
response_format | json_object em modelos que listam o campo; indisponível nos demais |
user, metadata, store | Aceitos e ignorados |
n | 1 é ignorado; qualquer outro valor é recusado com 400 em modelos que não listam o campo |
logprobs, top_logprobs, logit_bias e qualquer outro campo não listado pelo modelo | Recusados com 400 |
Para consultar os campos de um modelo e seus valores permitidos antes do envio, execute o comando em Exemplos de chat: antes de começar.
Respostas e streams
Uma resposta sem streaming é um objeto normal chat.completion; portanto, reply.choices[0].message.content e reply.usage funcionam como antes. As diferenças são pequenas:
reasoning_content. Modelos que raciocinam antes de responder retornam esse texto emmessage.reasoning_content, oudelta.reasoning_contentem streaming, junto acontent. Ele já conta emusage.completion_tokens.- O ID é o ID da solicitação.
idtem o formatochatcmpl-req_..., e o cabeçalho de respostax-request-idcontém o mesmo valorreq_.... Informe-o ao contatar o suporte. Um segundo cabeçalho,X-Spicy-Task-Id, identifica a tarefa que contém a cobrança; veja Custo e uso. - Sem
system_fingerprinte semlogprobs.
Streams usam os mesmos eventos chat.completion.chunk, terminam com data: [DONE] e seguem as regras da OpenAI para uso:
- Uso somente quando solicitado. Sem
stream_options.include_usage, nenhum bloco contémusage, e todos têm uma listachoicesnão vazia. Com"include_usage": true, um bloco extra com listachoicesvazia eusagefinal chega imediatamente antes de[DONE]; todos os demais têmusage: null. Código escrito com base nos exemplos de streaming da OpenAI funciona sem alterações; verificarchoicesantes de lerchoices[0]mantém a segurança no bloco final. - Um
finish_reasonpor escolha. Cada escolha termina com exatamente um bloco que contém seufinish_reason, independentemente do modelo chamado. - Linhas de manutenção da conexão. Enquanto um modelo ainda raciocina, o stream envia comentários
: keep-alivepara manter a conexão aberta. As bibliotecas OpenAI os ignoram; um parser escrito por você também precisa ignorá-los.
Para respostas longas ou reasoning_effort alto, use streaming. Uma solicitação sem streaming que ultrapassa cerca de 30 segundos é mantida aberta com espaços em branco antes do corpo JSON e o cabeçalho X-Spicy-Keepalive: 1, de modo que as bibliotecas OpenAI ainda consigam interpretá-la; porém, se falhar depois disso, o objeto de erro chega no corpo de uma resposta 200, que as bibliotecas não tratam como exceção. Veja Tempos limite e streaming.
Erros
Os erros usam o formato OpenAI, {"error": {"message", "type", "param", "code"}}, com os códigos HTTP habituais; por isso, as bibliotecas OpenAI lançam suas exceções normais. param identifica o campo relacionado ao erro, como caminho com pontos, por exemplo temperature ou messages.1.content.0; code diferencia erros que compartilham status: unsupported_parameter, um campo ou parte de mensagem que o modelo não aceita; invalid_request, um campo ausente ou valor não aceito; content_rejected, uma recusa por política de conteúdo, também 400; e model_not_found, 404. Uma diferença em relação à OpenAI: 402 é usado quando seu saldo ou um limite de gastos não cobre a solicitação. A tabela completa, com as ações para cada código, está em Exemplos de chat: quando algo dá errado.
As bibliotecas OpenAI repetem respostas 429 e 5xx por conta própria. Isso é seguro aqui: uma solicitação que falha não é cobrada. Se você enviar um Idempotency-Key, informe-o por chamada e leia antes Chaves de idempotência em endpoints de texto.
Custo e uso
As respostas informam a contagem de tokens em usage, não um preço. As tarifas são por milhão de tokens de entrada, entrada em cache e saída e estão na página de cada modelo e no catálogo. Para descobrir o custo real de uma chamada, leia o cabeçalho de resposta X-Spicy-Task-Id, com valor job_…, presente em toda solicitação aceita, com ou sem streaming, e consulte a tarefa com GET /api/v1/jobs/recordInfo?taskId=<job_…>: cost é final quando settled é true. Os itens de GET /api/v1/jobs também contêm o requestId da chamada que os criou, o cabeçalho X-Request-Id, e Console → Logs mostra as mesmas tarefas. A leitura do cabeçalho pelas bibliotecas OpenAI está em Exemplos de chat: consultar o custo de uma chamada; veja também Orçamentos e faturamento.
Antes da execução, é reservado do saldo um valor que cobre suas mensagens mais o max_tokens completo de saída, ou o padrão do modelo quando omitido. Você só paga pelo uso efetivo, mas a reserva precisa caber primeiro; saldo baixo combinado com max_tokens alto retorna 402. Defina um max_tokens realista ou limites de gastos na chave; veja Faturamento.
Endpoints que não existem aqui
A geração de texto está disponível como OpenAI Chat Completions, OpenAI Responses, Anthropic Messages e Google Gemini; os detalhes estão em Texto e streaming. Estes endpoints OpenAI não existem e retornam 404: /v1/embeddings, o legado /v1/completions, /v1/images/*, /v1/audio/*, /v1/moderations, /v1/files, /v1/batches, /v1/assistants e fine-tuning. Modelos de imagem, vídeo e áudio são chamados pela API de tarefas; vídeos também por /v1/videos.
Para quem vem do OpenRouter
O OpenRouter também usa o formato OpenAI, portanto as três mudanças acima se aplicam: substitua a URL base https://openrouter.ai/api/v1 por https://api.spicyapi.ai/v1 e troque a chave e os IDs dos modelos. Depois, remova os campos exclusivos do OpenRouter; aqui eles retornam 400:
| Campo OpenRouter | O que fazer |
|---|---|
models (lista de alternativas) e route | Remova. Envie um único ID de modelo; a SpicyAPI determina como atendê-lo |
provider | Remova. Não há seleção por solicitação de quem executa o modelo |
reasoning (objeto como {"effort": "high"}) | Convertido para reasoning_effort na raiz (enabled: false vira none quando o modelo oferece essa opção); outras chaves internas são ignoradas. Enviar reasoning_effort diretamente é mais claro |
usage ({"include": true}) | Remova. Respostas sem streaming sempre incluem usage; em streams, envie stream_options: {"include_usage": true} |
transforms, plugins | Remova |
Os cabeçalhos HTTP-Referer e X-Title não causam problemas e são ignorados.
Lista de verificação
- URL base
https://api.spicyapi.ai/v1e uma chave da SpicyAPI. - ID de modelo copiado do catálogo em tempo real e terminado em
/chat. - Campos da solicitação conferidos com o
inputSchemado modelo; campos não aceitos que não são de amostragem removidos; cabeçalhosX-Spicy-Ignored-ParamseX-Spicy-Adjusted-Paramsconferidos uma vez para identificar alterações. - Se você envia
stream_options.include_usage, os laços de streaming ignoram o bloco final cuja listachoicesestá vazia. max_tokensdefinido em um valor realista ou deixado no padrão do modelo, com limites de gastos na chave.- Solicitações longas ou com maior esforço usam
stream: true.
Continue lendo
Exemplos de chat · Texto e streaming · Catálogo de modelos e schemas · Autenticação · Faturamento
Exemplos de chat
Programas completos para conversas, roleplay, streaming na sua interface e chamadas de ferramentas, em Python, Node.js e curl.
Tutorial de imagem para vídeo
Comece com uma imagem do seu computador e termine com um arquivo de vídeo baixado. Cada passo mostra a CLI, o SDK para TypeScript e o HTTP puro.

