spicyapiDocumentação
Texto e chat
Conteúdo principal

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çãoValor na SpicyAPI
URL basehttps://api.spicyapi.ai/v1
Chave de APIUma chave da SpicyAPI, que começa com sk-spicy-, enviada como Authorization: Bearer
ModeloID 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=text lista somente modelos de texto com seus preços e inclui o inputSchema de cada modelo com includeSchema=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=text para obter apenas modelos de texto. Além dos quatro campos OpenAI, cada item tem modality e task; modelos de chat também têm context_length, max_output_tokens e supported_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_penalty e repetition_penalty só 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 para n: 1, stop vazio, stop em um modelo sem sequências de parada e reasoning_effort em um modelo sem níveis de raciocínio. A resposta informa o que foi ignorado no cabeçalho X-Spicy-Ignored-Params, por exemplo frequency_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 stop maior que o permitido mantém suas primeiras entradas; um nível reasoning_effort nã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 como name=value no cabeçalho X-Spicy-Adjusted-Params, por exemplo reasoning_effort=low, temperature=1.
  • Qualquer outro campo não listado pelo modelo é recusado com 400, code: "unsupported_parameter", param definido como o campo e uma mensagem que o identifica, como logprobs: 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.
CampoNa SpicyAPI
messagesMesmo 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_tokensAmbos 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_optionsAceitos, com as regras de uso da OpenAI: um stream só inclui uso quando include_usage é true; veja Respostas e streams
tools, tool_choiceAceitos 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_callsSomente em modelos que listam o campo
reasoning_effortNa 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_penaltyUsados 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
seedSomente em modelos que listam o campo
stopUsado em modelos que o listam, reduzido à quantidade máxima do modelo; ignorado em modelos sem sequências de parada e quando vazio
response_formatjson_object em modelos que listam o campo; indisponível nos demais
user, metadata, storeAceitos e ignorados
n1 é 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 modeloRecusados 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 em message.reasoning_content, ou delta.reasoning_content em streaming, junto a content. Ele já conta em usage.completion_tokens.
  • O ID é o ID da solicitação. id tem o formato chatcmpl-req_..., e o cabeçalho de resposta x-request-id contém o mesmo valor req_.... 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_fingerprint e sem logprobs.

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ém usage, e todos têm uma lista choices não vazia. Com "include_usage": true, um bloco extra com lista choices vazia e usage final chega imediatamente antes de [DONE]; todos os demais têm usage: null. Código escrito com base nos exemplos de streaming da OpenAI funciona sem alterações; verificar choices antes de ler choices[0] mantém a segurança no bloco final.
  • Um finish_reason por escolha. Cada escolha termina com exatamente um bloco que contém seu finish_reason, independentemente do modelo chamado.
  • Linhas de manutenção da conexão. Enquanto um modelo ainda raciocina, o stream envia comentários : keep-alive para 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 OpenRouterO que fazer
models (lista de alternativas) e routeRemova. Envie um único ID de modelo; a SpicyAPI determina como atendê-lo
providerRemova. 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, pluginsRemova

Os cabeçalhos HTTP-Referer e X-Title não causam problemas e são ignorados.

Lista de verificação

  1. URL base https://api.spicyapi.ai/v1 e uma chave da SpicyAPI.
  2. ID de modelo copiado do catálogo em tempo real e terminado em /chat.
  3. Campos da solicitação conferidos com o inputSchema do modelo; campos não aceitos que não são de amostragem removidos; cabeçalhos X-Spicy-Ignored-Params e X-Spicy-Adjusted-Params conferidos uma vez para identificar alterações.
  4. Se você envia stream_options.include_usage, os laços de streaming ignoram o bloco final cuja lista choices está vazia.
  5. max_tokens definido em um valor realista ou deixado no padrão do modelo, com limites de gastos na chave.
  6. 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

Nesta página