Guia: gerar e editar imagens
Como os endpoints de imagem se comportam como categoria — as duas formas de requisição, as três maneiras de escrever o tamanho da saída, o que uma imagem de referência acrescenta à conta, pesos de adaptação e como ler uma recusa.
Este guia trata da categoria dos endpoints de imagem, não de um modelo específico. Explica o que não muda entre eles e, mais útil ainda, os pontos em que divergem a ponto de um corpo de requisição escrito para um não sobreviver ao seguinte.
Não há aqui uma tabela de parâmetros, e isso é proposital. Parâmetros têm uma única fonte: o inputSchema que o catálogo ao vivo devolve para o modelo exato que você vai chamar. Esta página mostra como ler esse esquema e o que ele não diz em voz alta.
| Se você quer | Leia antes |
|---|---|
| Sua primeira imagem, da chave ao arquivo, de ponta a ponta | Início rápido |
| O mesmo percurso para vídeo, passo a passo | Tutorial: transformar uma foto em vídeo |
| Todos os campos de um modelo específico | O inputSchema dele — veja Catálogo de modelos e esquemas |
| Levar um rosto ou uma cabeça de uma foto para outra | Troca de sujeito |
MODEL_ID_FROM_CATALOG é um espaço reservado
Todos os exemplos abaixo escrevem MODEL_ID_FROM_CATALOG onde entra um identificador de modelo real. Pegue esse identificador no catálogo ao vivo autenticado — GET /api/v1/models?modality=image — e exija enabled: true e available: true antes de gastar qualquer coisa. Ler o catálogo é gratuito. Enviar o próprio espaço reservado devolve apenas um erro "not found".
Duas formas de requisição
O trabalho com imagens chega como duas tarefas, e o catálogo as separa:
- Endpoints de geração terminam em
/text-to-image. Você manda palavras e recebe uma imagem. - Endpoints de edição terminam em
/edit. Você manda também pelo menos uma imagem.
A edição tem duas grafias aceitas, e vale conhecer as duas antes de filtrar o catálogo: os endpoints são publicados com o sufixo edit, enquanto o array tasks os classifica como image-to-image. O parâmetro de consulta task aceita qualquer uma delas. Veja Catálogo de modelos e esquemas.
As duas formas usam o mesmo envelope de criação, o mesmo ciclo de vida de tarefa e o mesmo formato de resultado que qualquer outro modelo de mídia. O que muda é o input.
prompt é obrigatório nas duas
É tentador supor que um endpoint de edição dá conta só com a imagem e que prompt ali seria opcional. Nos endpoints que aceitam prompt, não é: prompt está na lista required da raiz nas duas formas, e omiti-lo é recusado antes de qualquer cobrança.
{
"model": "MODEL_ID_FROM_CATALOG",
"input": {
"image_urls": ["spicy://f/fil_01k3m8x9q2z4v7n5p6r8s0t1w3"]
}
}prompt: is requiredA imagem diz de onde você parte; o prompt diz o que fazer com ela. Um endpoint de edição que não recebeu instrução nenhuma não tem sobre o que agir.
Há uma exceção, e ela é uma família inteira, não um caso solto: alguns endpoints de edição não aceitam prompt nenhum e recebem só imagens, porque a operação é fixada pelo próprio endpoint. Eles pertencem ao conjunto de ferramentas de troca de sujeito e trazem um campo toolkit no catálogo, o que permite reconhecê-los; veja Troca de sujeito. Neles, prompt: is not a supported field é a resposta certa, não um defeito.
O campo da imagem de entrada tem três nomes
Qual deles um endpoint usa faz parte do esquema dele e não se deduz do tipo de tarefa:
| Campo | Forma | O que costuma significar |
|---|---|---|
image_url | uma referência de imagem | Há exatamente uma imagem de sujeito, e o endpoint não tem noção de uma segunda |
image_urls | array de referências de imagem | As imagens são o material editado ou combinado, na ordem a que a instrução se refere |
reference_image_urls | array de referências de imagem | As imagens orientam uma geração que de outro modo seria só de texto — são guia, não o sujeito |
O que surpreende é reference_image_urls: ele aparece em endpoints de geração, então um endpoint /text-to-image pode perfeitamente aceitar imagens. Não presuma que "text-to-image" queira dizer "sem imagens na requisição".
Uma referência de imagem é uma URL HTTPS pública, uma URI spicy:// confirmada após um envio, ou uma URI data:image/…;base64,… pequena. Para envios veja Mídia; para os nomes canônicos, o contrato Spicy Schema.
Os limites dos arrays variam muito mais do que se imagina
Os arrays acima trazem minItems e maxItems, o servidor aplica os dois, e os números diferem por endpoint — hoje vão de um array que aceita exatamente uma entrada até arrays que aceitam dez. Um endpoint da mesma família, com o mesmo nome de campo, pode ter outro teto.
image_urls: must contain at least 1 items
image_urls: must contain at most 3 itemsLeia minItems e maxItems no esquema do modelo que você vai chamar. Uma tabela de parâmetros que mostra um array como um mero array, sem a forma das entradas e sem limite de tamanho, é exatamente a linha com que o leitor não consegue fazer nada — e é justamente por isso que este site não publica nenhuma.
O tamanho da saída se escreve de três jeitos
É a primeira coisa em que quase todo mundo tropeça ao apontar um corpo de requisição existente para um segundo endpoint.
No catálogo de imagem convivem três grafias, e cada endpoint aceita exatamente uma:
| Grafia | Campos | Observações |
|---|---|---|
| Separada | aspect_ratio e resolution | A forma do quadro e o degrau de pixels são escolhidos em separado |
| Predefinição | size | Um valor de enumeração fixa os dois de uma vez |
| Explícita | width e height | Pixels inteiros, cada um com seu minimum e seu maximum |
A grafia separada é a mais comum nos endpoints recentes e a mais útil das três: aspect_ratio compõe o quadro antes de gerar, em vez de recortar depois, então você muda a forma sem mexer no orçamento de pixels.
Um campo da grafia errada é recusado, não ignorado
Todo inputSchema de imagem define additionalProperties: false. Um campo que o endpoint não lista é rejeitado, ou seja, um corpo de requisição trazido de outro endpoint falha de vez em vez de cair silenciosamente num valor padrão.
Falha nas duas direções, e a mensagem nomeia o campo:
size: is not a supported field
aspect_ratio: is not a supported fieldNenhum dos dois é cobrado. O custo não é dinheiro, é a suposição: você não escreve um objeto input e o aponta para vários endpoints de imagem, e não existe grafia "de compatibilidade" que todos aceitem.
Qual delas seleciona uma faixa de preço
Num endpoint de grafia separada, os dois campos costumam ter papéis diferentes na conta:
resolutionnormalmente seleciona uma faixa de preço. Aparece no orçamento comovariant, e mexer nele mexe no valor.aspect_rationormalmente não. Reenquadrar não é redimensionar.
O "normalmente" dessas duas frases está trabalhando de verdade. A resposta legível por máquina está no próprio campo: x-ui.affects_price: true e, quando o endpoint declara, a presença em x-pricing.variantFields. Consulte o esquema, não o hábito. Veja Ler os preços e a referência de anotações no contrato Spicy Schema.
Os valores permitidos também são por endpoint. Uma recusa lista todos eles, o que é um jeito gratuito de vê-los:
aspect_ratio: must be one of: "1:1", "16:9", "9:16", "4:3", "3:4", "3:2", "2:3", "21:9"Útil enquanto você explora, mas não substitui ler enum, porque o esquema traz também o default que vale quando você omite o campo.
O que uma imagem de referência acrescenta à conta
Este é o item silencioso, e merece uma seção só dele porque nada na resposta aponta para ele.
A aritmética usual de um modelo de mídia é preço × quantidade, com a quantidade sendo o valor de quantityField na sua entrada, ou uma unidade quando quantityField está vazio. Nos endpoints de imagem, quantityField costuma ser num_outputs, e ele se comporta como se espera: peça quatro imagens e o orçamento mostra quantity igual a 4. Essa dimensão é honesta — dá para vê-la se mexer.
Alguns endpoints acrescentam por cima uma cobrança separada para cada imagem de entrada, e essa não dá para ver se mexendo. Peça dois orçamentos do mesmo endpoint, um com uma única referência e outro com o array cheio, e você recebe:
- a mesma
unit; - a mesma
quantity; - a mesma
variant; - um valor diferente.
Três dos quatro campos com que normalmente se monta uma estimativa ficam exatamente onde estavam. Uma estimativa calculada como preço × quantidade fica abaixo do real, e nada na resposta sinaliza isso.
A assinatura legível por máquina
Um campo marcado com x-ui.affects_price: true que não é o quantityField nem está em x-pricing.variantFields mexe no valor sem selecionar faixa. Para um endpoint assim, leia o pricing do catálogo como um piso e tire o valor de um orçamento.
É a mesma regra enunciada em Catálogo de modelos e esquemas, e a edição de imagem é onde você mais vai esbarrar nela: o array de imagens de entrada costuma vir com essa marca.
Quanto se acrescenta depende do endpoint e não segue proporção fixa. O catálogo hoje tem endpoints em que encher o array custa várias vezes o preço de uma referência só, e endpoints em que referências extras não custam nada — sem jeito de distinguir além de perguntar. Então não estime o preço do array por analogia com um endpoint irmão, nem deduza um array cheio a partir de um orçamento de uma referência.
Orce o corpo que você vai enviar de fato
curl -sS https://api.spicyapi.ai/api/v1/jobs/quote \
-H "Authorization: Bearer $SPICY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"MODEL_ID_FROM_CATALOG","input":{"prompt":"your instruction","image_urls":["spicy://f/fil_01k3m8x9q2z4v7n5p6r8s0t1w3"]}}'Um orçamento roda a mesma validação da criação da tarefa, não custa nada, não inicia nada e não reserva nada. Devolve estimatedCost, maxCharge, quantity e unit para aquele corpo exato. Mudou o array, orce de novo. Os detalhes, e como se recuperar quando a resposta se perde, estão em Orçamentos e compatibilidade; onde um orçamento entra numa integração real, em Integração em produção.
Pesos de adaptação
Alguns endpoints deixam você aplicar pesos ajustados por você mesmo sobre o modelo base. O campo canônico é loras, ou high_noise_loras e low_noise_loras quando um endpoint expõe seus estágios em separado.
Um array de objetos, com regras próprias lá dentro
Esses campos trazem x-ui.widget: "object-list": um array cujo items é um esquema de objeto completo por si só. Tem required próprio, additionalProperties: false próprio, e tipo, faixa e default próprios para cada subcampo. Uma entrada típica nomeia um conjunto de pesos e com que força aplicá-lo.
{
"model": "MODEL_ID_FROM_CATALOG",
"input": {
"prompt": "your scene",
"loras": [
{ "path": "https://weights.example.com/style.safetensors", "scale": 1 },
{ "path": "https://weights.example.com/character.safetensors", "scale": 0.6 }
]
}
}A forma de uma entrada não é fixa entre endpoints. O que conta como arquivo de pesos — um endereço direto ou uma chave de uma biblioteca embutida — é estreitado pelo subesquema items de cada endpoint, assim como os nomes dos subcampos, quantas entradas cabem e quais são obrigatórias. Leia items, não este exemplo. O contrato Spicy Schema descreve o widget; Catálogo de modelos e esquemas mostra como transformá-lo numa tabela aninhada.
Uma regra interna dessas entradas é fácil de ler errado: os valores padrão alcançam o interior das entradas do array. Um subcampo que está em items.required e também tem default é na prática opcional — omita-o e o padrão é preenchido antes da validação. Um subcampo sem padrão é recusado quando falta. Ou seja, items.required não quer dizer "precisa aparecer no JSON que eu mando".
De onde vêm os pesos
O path aceita uma de três coisas:
- Um link público para o arquivo
.safetensorsou, onde o endpoint aceitar, um id de repositório do Hugging Face comoowner/repo. O modelo baixa o arquivo na hora da geração, então a primeira chamada com pesos novos é visivelmente mais lenta que as seguintes. - Um upload. Envie o arquivo como
application/octet-streampelo fluxo de upload direto, até 2 GiB, e use o URIspicy://f/…devolvido. O commit confere se é um arquivo safetensors completo. - Uma importação. Para um arquivo que exige login, como um de um repositório privado ou restrito do Hugging Face ou um arquivo do Civitai protegido por chave de API, envie o link e um token de acesso para
POST /api/v1/files/import. A SpicyAPI baixa o arquivo uma única vez e devolve um URIspicy://f/…. O token é usado só nesse download e nunca é armazenado.
curl -sS https://api.spicyapi.ai/api/v1/files/import \
-H "Authorization: Bearer $SPICY_API_KEY" \
-H "Content-Type: application/json" \
--data "$(jq -n --arg url "https://huggingface.co/OWNER/REPO/blob/main/your_lora.safetensors" --arg token "$HF_TOKEN" '{url: $url, token: $token}')"url também pode ser o id do repositório (OWNER/REPO) ou a página do repositório, quando ele contém exatamente um arquivo .safetensors. A resposta tem o mesmo formato da de um commit; coloque data.uri em loras[].path. Pesos enviados e importados ficam guardados por um dia, como qualquer outro upload, e nunca são compartilhados com outras contas. Depois disso, envie ou importe de novo.
O envio confere o link, não os pesos dentro dele
Antes de a tarefa ser criada, cada link de pesos é sondado. Um link que exige login, devolve 404 ou abre uma página web é recusado com um 400, sem nenhuma cobrança, e a mensagem aponta a entrada. Todo o resto passa; por isso um host que caia depois, ou pesos que falhem ao carregar, ainda chegam a você como falha de tarefa.
Os problemas que o sondeio não consegue ver voltam depois, como falha de tarefa com um errorCode entre as nove classes públicas da plataforma e uma mensagem que explica o que deu errado; o valor retido para a tarefa é devolvido. Veja Erros.
O pior caso é mais silencioso ainda: pesos construídos para outro modelo base podem carregar sem falhar e quase não mudar a saída. Não há erro para ler, só uma imagem parecida com a do modelo base.
Então confira na hora do deploy
Tire a conferência do caminho da requisição e leve-a para o que roda quando você publica:
- Alcançabilidade. Uma requisição
HEADem cada endereço de pesos que você vai colocar em produção. A plataforma sonda cada link quando a tarefa é criada, mas um link que funciona hoje pode parar na semana que vem, e uma conferência no deploy pega isso antes do seu tráfego. - Efeito. Uma geração por arquivo de pesos novo, comparada com o mesmo prompt e a mesma semente sem ele. Se os dois forem indistinguíveis, o arquivo não está fazendo o que você imagina — confirme antes que um lote inteiro dependa disso.
- Estabilidade. Seus endereços precisam continuar funcionando enquanto estiverem em produção. Um arquivo de pesos que some vira falhas de tarefa sem causa aparente, dias depois do deploy a que elas serão atribuídas.
Os conselhos específicos de cada endpoint sobre esses campos — contra o que um valor é realmente medido, qual ajuste deixa a chamada perceptivelmente mais lenta, quais entradas o esquema aceita e você vai se arrepender — vivem em paramNotes, no catálogo público, indexados por nome de parâmetro e disponíveis nos dez idiomas da documentação. É prosa, nunca regra; quem decide o que é aceito continua sendo o inputSchema. Veja Catálogo de modelos e esquemas.
Assíncrono, como todo modelo de mídia
Endpoints de imagem são tarefas, não chamadas de requisição e resposta. Você cria uma tarefa e depois recolhe o resultado por sondagem ou por webhook; queued e running não são terminais, e succeeded, failed e expired são. A autoridade sobre os estados é Tarefas assíncronas.
Imagens terminam mais rápido que vídeos, o que tenta a escrever a chamada como se fosse síncrona. Resista: aqui uma sondagem limitada é curta o bastante para parecer síncrona, mas o código ainda precisa lidar com uma tarefa que continua rodando quando o orçamento de espera acaba, e entregar isso a um webhook escala melhor do que manter uma requisição aberta. As consultas de status usam a capacidade de leitura da conta, independentemente do envio de tarefas. Consultas excessivas podem atingir o limite de leitura de status; veja Limites de solicitações.
Ler o resultado
Uma tarefa bem-sucedida lista seus arquivos em output.assets[]. Três coisas sobre essa lista nos endpoints de imagem:
- Ela pode ter mais de um arquivo. Quando
num_outputsé maior que um, você recebe um recurso por imagem. Escreva o tratador como um laço desde o início; um tratador que lêassets[0]descarta em silêncio tudo o mais que você pagou. - Quem decide a extensão é
mime, não o nome do endpoint nem o prompt. Quando um endpoint expõeoutput_format, esse campo escolhe a codificação; quando não expõe, a codificação é própria do endpoint emimeé o único jeito de saber o que você recebeu. - Um arquivo ainda em cópia traz
pending: truee nenhumaurl. Os resultados são copiados para o armazenamento da própria SpicyAPI antes de serem entregues, o que leva alguns segundos depois que a tarefa reporta sucesso. Leia a tarefa de novo em vez de tratar o link ausente como falha.
Os links de download valem cerca de vinte minutos e não pedem chave de API; leia a tarefa de novo para obter um novo. Arquivos gerados ficam guardados por no máximo catorze dias, menos se a retenção da sua conta for mais curta. Veja Resultados e downloads e Retenção e destruição.
Ler uma recusa
Uma requisição recusada não custa nada e não cria nada, então o endpoint de orçamento serve também como validador gratuito do corpo que você está prestes a enviar.
Todos os problemas são relatados, não só o primeiro. A mensagem junta todos com ; , então um único orçamento recusado pode contar tudo o que está errado:
prompt: is required; loras.0.path: is required; loras.0.scale: must be at most 4; bogus: is not a supported fieldUm problema dentro de um array aponta para a entrada. O ponteiro é campo.índice.subcampo, com índice começando em zero:
loras.1.scale: must be at most 4Repasse esse ponteiro para quem vai corrigir. Com várias entradas em jogo, saber qual delas saiu da faixa é a diferença entre um conserto de uma linha e uma bissecção.
As formas que você vai encontrar:
| Mensagem | O que significa | O que fazer |
|---|---|---|
X: is required | X está na lista required da raiz | Acrescente. Em endpoints de edição costuma ser prompt ou o campo de imagem |
X: is not a supported field | X não está em properties; campos desconhecidos são recusados, nunca ignorados | Remova, ou use a grafia deste endpoint — veja Tamanho da saída |
X: must be one of: … | O valor está fora de enum | A mensagem lista os valores aceitos; enum e default estão no esquema |
X: must contain at least N items | Abaixo de minItems | Um array que o endpoint exige não pode ser enviado vazio |
X: must contain at most N items | Acima de maxItems | Leia o teto deste endpoint; ele não é igual em todos |
X: must be an array | O valor não tem o tipo declarado | Coloque a entrada única dentro de uma lista |
X: must be at most N characters | Acima de maxLength | Prompts também têm teto por endpoint |
X.<n>.<sub>: is required | Um subcampo da entrada <n> é obrigatório e não tem padrão | Acrescente só naquela entrada |
X.<n>.<sub>: is not a supported field | O esquema da entrada define additionalProperties: false | Tire essa chave daquela entrada |
X.<n>.<sub>: must be at most N | O subcampo saiu da faixa dele | Limite; as faixas são por subcampo, não por array |
Se a recusa chega quando você esperava que funcionasse, a causa mais comum é um esquema desatualizado. Releia o catálogo depois de um 400 e nunca guarde um retrato permanente — Cachear o catálogo diz por quanto tempo uma cópia vale. O resto está em Solução de problemas.
Próximos passos
Catálogo de modelos e esquemas
O catálogo ao vivo, as faixas de preço e sua própria tabela de parâmetros a partir do inputSchema
Contrato Spicy Schema
Nomes canônicos de campos, anotações x-ui, campos condicionais e o formato do resultado
Mídia
Enviar uma imagem e transformá-la numa referência spicy://
Orçamentos e compatibilidade
Precificar uma requisição exata antes de pagar por ela
Troca de sujeito
Os endpoints de edição que recebem imagens e nenhum prompt
Tutorial: transformar uma foto em vídeo
O mesmo percurso para vídeo, com a CLI, o SDK e HTTP puro
Erros
Todos os códigos de erro e em quais vale a pena tentar de novo
Integração em produção
O que acrescentar antes que usuários reais dependam disso
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.
Substituição de pessoas
Um kit, dois formatos. Modelos de uma chamada trocam um rosto ou uma cabeça em uma única requisição; modelos de duas chamadas analisam o clipe antes para que você escolha quem muda.

