Imagine um serviço que envia notas de reparo a um modelo DeepSeek e espera um objeto JSON em troca: o que foi inspecionado, o que foi recomendado, se o cliente aprovou e se o trabalho foi feito. Hoje ele chama deepseek-chat, deepseek-flash ou uma rota do OpenRouter, e você quer que rode no DeepSeek V4.1 Flash pela SpicyAPI (spicyapi.ai).
Quatro mudanças resolvem: use o ID de modelo deepseek/v4.1-flash/chat, apague todo campo que este modelo não aceita, escolha um reasoning_effort e dê a max_tokens uma folga de verdade, porque o raciocínio é pago com o mesmo orçamento. Depois, rode um pequeno caso de teste antes do tráfego real. Use a página do modelo V4.1 Flash para experimentar a requisição.
O que mudar na requisição
URL base, chave e ID do modelo são a parte fácil (guia de migração). É no corpo da requisição que as coisas quebram:
| No seu cliente atual | Na SpicyAPI |
|---|---|
URL base https://api.deepseek.com e uma chave da DeepSeek | https://api.spicyapi.ai/v1 e uma chave que começa com sk-spicy- |
Modelo deepseek-flash, ou os antigos deepseek-chat, deepseek-reasoner, deepseek-v4-flash, deepseek-v4-pro | deepseek/v4.1-flash/chat |
thinking: {"type": "disabled"}, o comportamento que o deepseek-chat dava | reasoning_effort: "none" pula a etapa de raciocínio |
| Thinking ligado (o padrão) | Omita reasoning_effort para high, ou defina low para trabalho em volume |
reasoning_effort com max, medium, minimal, xhigh | Só none, low e high são aceitos; qualquer outro valor retorna 400 invalid_request |
temperature, top_p, presence_penalty, frequency_penalty | Apague; 400 unsupported_parameter |
parallel_tool_calls | Apague; não exposto neste modelo |
stop | Mantenha: até 4 sequências, e o texto correspondente fica fora da resposta |
Partes image_url ou de arquivo | Mantenha as partes image_url para imagens; remova as partes de PDF/arquivo e extraia antes o texto delas |
models, route, provider, usage, transforms, plugins do OpenRouter e um objeto reasoning | Apague os seis primeiros; troque reasoning pelo reasoning_effort de nível superior |
Este modelo hospedado não tem controle de amostragem, nem mesmo com reasoning_effort: "none". Valide os resultados das tarefas em vez de depender de temperature: 0 para repetibilidade.
O que continua igual: messages (conteúdo de texto ou imagem, até 256), tools (até 128 definições) com tool_choice (auto, none, required ou uma função nomeada), response_format com {"type": "json_object"}, stop, max_tokens (padrão 32.768, máximo 384.000) e uma janela de entrada de 1.048.576 tokens que conta todas as mensagens enviadas, incluindo o prompt de sistema e resultados de ferramentas anteriores.
A SpicyAPI nunca descarta um campo em silêncio: o 400 nomeia o campo e nada é cobrado. A API própria da DeepSeek aceita e ignora temperature e os campos de penalidade no modo thinking, então um cliente antigo pode carregá-los sem ninguém perceber.
A falha silenciosa é o nome do modelo. Na API própria da DeepSeek, o V4 Flash foi aposentado e deepseek-v4-flash redireciona temporariamente para o V4.1 Flash; o nome atual é deepseek-flash. O V4 Pro é diferente: a DeepSeek anunciou inicialmente a troca para o Flash em 14 de setembro, mas seu registro de alterações confirmou depois que o serviço V4 Pro continuaria com a cobrança inalterada. Não migre presumindo que toda chamada antiga ao Pro já roda no Flash.
Na SpicyAPI, as três rotas de chat da DeepSeek têm contratos publicados distintos: manter deepseek/v4-flash/chat na sua configuração é uma requisição válida, com teto de saída de 65.536 tokens e preços mais altos. Um alias do serviço nativo não muda esse contrato hospedado. Use deepseek/v4.1-flash/chat de forma explícita nesta migração.
Esta rota aceita imagens por partes image_url, mas não partes de PDF/arquivo. Extraia o texto de um PDF antes de enviá-lo; os exemplos abaixo, só com notas de reparo em texto, não precisam de entrada de imagem.
Dê espaço à resposta
O modelo raciocina antes de responder, e esse raciocínio sai de max_tokens. O esforço padrão é high, e um teto de 120 tokens não retornou conteúdo algum. O max_tokens padrão é 32.768, e uma entrega mais longa é truncada. Dimensione o teto pela resposta visível mais alguns milhares de tokens de espaço para pensar, não pelo máximo de 384.000.
Comece a extração com esforço Low. Se ele perder uma distinção necessária, tente High naquele registro em vez de encarecer todas as linhas. Para entradas grandes, use a interface de streaming.
Experimente primeiro no Playground
- Abra o DeepSeek V4.1 Flash, escolha Playground e defina o controle de esforço como Low.
- Coloque a instrução de formato JSON do script abaixo em System prompt e a nota de reparo R17 em Message to the model, e clique em Run model.
- Confirme que uma aprovação pendente não vira um reparo concluído antes de migrar seu app.
Rode um caso de teste que pega o erro caro
Uma nota de reparo é um bom caso de teste porque um resumo fluente pode estar errado de um jeito que custa dinheiro. “Recomendar substituição” não é aprovação do cliente, e aprovação não é trabalho concluído.
Coloque uma chave de API Keys em SPICY_API_KEY, salve este script como check_repair.py e execute python3 check_repair.py. O prompt de sistema mostra o formato do JSON e contém a palavra “json”, que o guia do modo JSON da DeepSeek pede.
import json
import os
import uuid
import urllib.request
import re
SOURCE = (
'Repair note R17: "Inspected the rear brake pads. Recommend replacement before '
'the next long ride. Customer approval is pending." No later work record is supplied.'
)
KEYS = {"inspected", "recommended_action", "authorized", "completed", "evidence"}
REQUIRED_QUOTES = (
"Inspected the rear brake pads.",
"Recommend replacement before the next long ride.",
"Customer approval is pending.",
)
def check(response: dict) -> list[str]:
choice = response["choices"][0]
problems = []
if choice.get("finish_reason") != "stop":
problems.append(f"finish_reason is {choice.get('finish_reason')!r}: the reply may be cut off")
content = (choice.get("message") or {}).get("content")
if not content or not content.strip():
return problems + ["empty content: reasoning may have used the whole max_tokens budget"]
try:
data = json.loads(content)
except json.JSONDecodeError as error:
return problems + [f"not valid JSON: {error}"]
if not isinstance(data, dict):
return problems + ["JSON must be an object"]
if missing := KEYS - data.keys():
problems.append(f"missing keys: {sorted(missing)}")
for key in ("inspected", "authorized", "completed"):
if data.get(key) is not None and type(data.get(key)) is not bool:
problems.append(f"{key} must be true, false or null")
if data.get("inspected") is not True:
problems.append("inspected should be true")
if data.get("authorized") is not False:
problems.append("authorized must be false: approval is pending")
if data.get("completed") is not None:
problems.append("completed must be null: no completion record is supplied")
action = data.get("recommended_action")
if not isinstance(action, str) or not action.strip():
problems.append("recommended_action must be a non-empty string")
else:
words = set(re.findall(r"[a-z]+", action.lower()))
# A lexical screen, not a proof of semantic equivalence.
required = {"rear", "long", "ride"}
compatible = (required <= words
and bool(words & {"replace", "replacement", "replacing"})
and bool(words & {"pad", "pads"})
and not words & {"not", "no", "never", "front", "after"})
if not compatible:
problems.append("review recommendation wording against R17")
quotes = data.get("evidence")
if not isinstance(quotes, list) or not quotes:
problems.append("evidence must be a non-empty list of quotes")
else:
problems += [f"invalid or unmatched quote: {q!r}" for q in quotes
if not isinstance(q, str) or not q.strip() or q not in SOURCE]
for expected in REQUIRED_QUOTES:
if not any(isinstance(q, str) and expected in q for q in quotes):
problems.append(f"missing supporting quote: {expected}")
return problems
body = {'model': 'deepseek/v4.1-flash/chat',
'messages': [{'role': 'system',
'content': 'Extract only facts stated in the source. Reply with one '
'JSON object shaped like {"inspected": null, '
'"recommended_action": null, "authorized": null, '
'"completed": null, "evidence": []}. inspected, '
'authorized and completed are true, false or null; use '
'null for anything the source does not state. evidence '
'lists exact quotes from the source that support every '
'non-null value.'},
{'role': 'user',
'content': 'Repair note R17: "Inspected the rear brake pads. '
'Recommend replacement before the next long ride. '
'Customer approval is pending." No later work record is '
'supplied.'}],
'max_tokens': 2048,
'reasoning_effort': 'low',
'response_format': {'type': 'json_object'}}
request = urllib.request.Request(
"https://api.spicyapi.ai/v1/chat/completions",
data=json.dumps(body).encode(),
headers={"Authorization": "Bearer " + os.environ["SPICY_API_KEY"],
"Content-Type": "application/json",
"Idempotency-Key": str(uuid.uuid4())},
)
with urllib.request.urlopen(request, timeout=90) as reply:
response = json.load(reply)
problems = check(response)
print(json.dumps(response.get("usage", {})))
if problems:
raise RuntimeError("; ".join(problems))
print(response["choices"][0]["message"]["content"])A decisão esperada é: inspecionado, aprovação ainda pendente e conclusão desconhecida. A recomendação precisa preservar as pastilhas traseiras e o prazo de antes do próximo passeio longo.
Adicione aprovação e conclusão como casos de teste separados
Não pare no caso de aprovação pendente. Use estes como modelos para casos adicionais: atualize SOURCE, REQUIRED_QUOTES e os valores esperados de aprovação/conclusão antes de rodar cada um. O verificador do R17 não roda diretamente contra R18 ou R19:
| Último registro | Authorized | Completed |
|---|---|---|
| R17: “Customer approval is pending.” Nenhum registro posterior | false | null |
| R18: “Customer approved the quoted replacement. Parts ordered; work scheduled for Thursday.” | true | null |
| R19: “Replaced pads; road check completed Thursday.” Vinculado ao serviço aprovado | true | true |
Guarde junto da fonte o ID do serviço, a ordem dos registros e as datas: o R18 substitui a aprovação pendente, mas agendar o trabalho não comprova a conclusão. Se uma nota de “feito” sem data não puder ser vinculada ao mesmo serviço, retorne o vínculo não resolvido em vez de mesclá-la ao R17.
A mesma divisão em três aparece no fluxo de produção de um criador: “Aprove este design de personagem”, “design aprovado” e “vídeo gerado a partir do design aprovado” são um pedido, uma decisão e uma execução. Peça ao prompt de extração essas etapas separadamente, com o registro que sustenta cada uma, e preserve qualquer conflito entre notas em vez de pedir ao modelo que escolha a frase que soa mais confiante.
Mantenha aprovação e conclusão separadas em fluxos com ferramentas
Um registro de aprovação não prova que o reparo aconteceu: consulte o registro de conclusão antes de mudar o status e mantenha aprovação e conclusão como campos separados no que suas ferramentas retornam.
Chat sem ferramentas não precisa de nada extra. Com tools na requisição, um cliente que reconstrói cada mensagem do assistente só com papel e conteúdo passa no primeiro turno e falha no segundo com um 400, então um teste de turno único esconde o bug. Anexe a mensagem do assistente exatamente como foi retornada, incluindo reasoning_content, depois adicione uma mensagem tool por chamada com o tool_call_id correspondente e limite o número de rodadas. Teste dois turnos, não um. As receitas de chat mostram o ciclo completo.
Deixe um Agent preparar a chamada
Para extração repetida de registros, peça ao seu Agent de programação que use o Skill da SpicyAPI: “Read deepseek/v4.1-flash/chat, then adapt the example to my input; show the request before running it.” A consulta pela CLI é spicyapi models get deepseek/v4.1-flash/chat --json; pelo MCP, é spicyapi_model_get com model: "deepseek/v4.1-flash/chat". As chamadas de chat usam um cliente compatível ou a API de chat; os comandos de tarefas de mídia não geram respostas de chat.
Recalcule a fatura
As tarifas são US$ 0,30 por milhão de tokens de entrada sem cache, US$ 0,006 por milhão de entrada em cache e US$ 1,20 por milhão de saída, uma tarifa única em qualquer tamanho de requisição. Um prompt de 40.000 tokens sem cache com 2.000 tokens de saída custa US$ 0,0144. Se 30.000 dos tokens de entrada forem cobrados como cache e 10.000 como sem cache, a mesma chamada custa US$ 0,00558. Tokens de raciocínio são cobrados como saída, então uma resposta visível curta ainda pode usar mais saída do que você espera; none é a única configuração que os mantém fora da fatura.
Duas comparações importam ao deixar a API própria da DeepSeek. Os preços de tabela dela são os mesmos US$ 0,30, US$ 0,006 e US$ 1,20 no horário de pico, mas fora do pico custam metade lá, e aqui há uma tarifa única: um lote que você agenda fora do pico na DeepSeek custa cerca do dobro na SpicyAPI pelos mesmos tokens. E a tarifa de cache é 1/50 da de entrada, mas só para um prefixo idêntico byte a byte de uma chamada para outra: um prompt de sistema de 1.500 tokens em 10.000 chamadas dá 15 milhões de tokens de entrada, US$ 4,50 sem cache e no máximo US$ 0,09 se todos fossem leituras de cache. Compare o usage retornado e o custo liquidado com essas estimativas. Um carimbo de data/hora ou instruções reordenadas podem quebrar um prefixo compartilhado; entrada inalterada, por si só, não garante um acerto de cache.
Onde cada rota DeepSeek cai
| V4.1 Flash | V4 Flash | V4 Pro | |
|---|---|---|---|
| ID do modelo | deepseek/v4.1-flash/chat | deepseek/v4-flash/chat | deepseek/v4-pro/chat |
| Saída máx. | 384.000 | 65.536 | 384.000 |
| Entrada / cache / saída por 1 M | US$ 0,30 / US$ 0,006 / US$ 1,20 | US$ 0,44 / US$ 0,014 / US$ 1,32 | US$ 1,32 / US$ 0,044 / US$ 3,96 |
parallel_tool_calls | Não exposto | Não exposto | Exposto |
As três aceitam 1.048.576 tokens de entrada, esforço none, low ou high (padrão high) e um max_tokens padrão de 32.768. Vindo de deepseek/v4-flash/chat, o corpo da requisição é idêntico, então só o ID muda: o teto de saída cresce quase seis vezes e todas as tarifas caem. Vindo do V4 Pro, você perde parallel_tool_calls e paga 77% menos pela entrada e cerca de 70% menos pela saída. Se o V4 Flash ou o V4 Pro é a melhor escolha para uma tarefa específica é outra decisão, tratada em DeepSeek V4 Flash vs Pro.
Se o seu app escreve ficção adulta
O V4.1 Flash está listado como sem censura na SpicyAPI, mas isso não é promessa de que ele complete todo pedido de ficção adulta. Em nossos testes de setembro de 2026, pedidos diretos de cenas maduras intensas voltaram suavizados todas as vezes, enquanto enquadrá-los como ficção ou definir o contexto no prompt de sistema rendeu mais deles por inteiro, embora não todos (detalhes na análise do V4.1 Flash). Se seus prompts foram escritos para um modelo que aceitava pedidos diretos, mova o enquadramento para a mensagem de sistema durante a migração e mantenha todo personagem adulto.
Corrija diretamente as falhas de migração
| Sintoma | Causa provável | Correção |
|---|---|---|
400 unsupported_parameter nomeando temperature, top_p, uma penalidade, parallel_tool_calls ou thinking | Campo herdado do cliente antigo | Apague |
content vazio ou finish_reason igual a length | max_tokens gasto em raciocínio, ou a resposta passou dele | Aumente max_tokens, reduza o esforço, tente de novo |
| O JSON é interpretado, mas informa trabalho concluído | Recomendação lida como conclusão | Mantenha campos separados, exija citações, rode o verificador |
| Erro no segundo turno de uma conversa com ferramentas | Mensagem do assistente reconstruída sem reasoning_content | Anexe a mensagem retornada sem alterações |
| 402 com saldo suficiente | A retenção 40201 excede o saldo, ou o limite de gasto 40202 foi atingido | Inspecione o erro: reduza um orçamento de saída excessivo para a retenção, ou revise os limites da chave/workspace |
Idempotency-Key reutilizada rejeitada (400 ou 409) | A chave pertence a uma requisição concluída ou a um corpo diferente | Um 400 para requisição concluída não consegue reproduzir a resposta; um 409 pode indicar corpo alterado. Reutilize a chave só para uma nova tentativa de rede idêntica dentro da janela dela; confira a liquidação da tarefa antes de escolher uma nova chamada paga |
| Uma chamada longa sem streaming cai perto de 100 segundos | Corte na borda da rede | Use streaming na resposta |
Troque sem perder a referência antiga
Mantenha o ID do modelo antigo na configuração e rode o caso de teste nos dois. Mova o tráfego quando o teste passar com o novo ID, suas próprias fontes validarem e o usage registrado bater com a aritmética acima.








