spicyapiDocumentação
Guias
Conteúdo principal

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ê querLeia antes
Sua primeira imagem, da chave ao arquivo, de ponta a pontaInício rápido
O mesmo percurso para vídeo, passo a passoTutorial: transformar uma foto em vídeo
Todos os campos de um modelo específicoO inputSchema dele — veja Catálogo de modelos e esquemas
Levar um rosto ou uma cabeça de uma foto para outraTroca 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.

Recusado por um endpoint de edição
{
  "model": "MODEL_ID_FROM_CATALOG",
  "input": {
    "image_urls": ["spicy://f/fil_01k3m8x9q2z4v7n5p6r8s0t1w3"]
  }
}
prompt: is required

A 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:

CampoFormaO que costuma significar
image_urluma referência de imagemHá exatamente uma imagem de sujeito, e o endpoint não tem noção de uma segunda
image_urlsarray de referências de imagemAs imagens são o material editado ou combinado, na ordem a que a instrução se refere
reference_image_urlsarray de referências de imagemAs 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 items

Leia 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:

GrafiaCamposObservações
Separadaaspect_ratio e resolutionA forma do quadro e o degrau de pixels são escolhidos em separado
PredefiniçãosizeUm valor de enumeração fixa os dois de uma vez
Explícitawidth e heightPixels 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 field

Nenhum 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:

  • resolution normalmente seleciona uma faixa de preço. Aparece no orçamento como variant, e mexer nele mexe no valor.
  • aspect_ratio normalmente 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

Precificar esta requisição exata antes de pagar por ela
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.

Duas entradas aplicadas juntas
{
  "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 .safetensors ou, onde o endpoint aceitar, um id de repositório do Hugging Face como owner/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-stream pelo fluxo de upload direto, até 2 GiB, e use o URI spicy://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 URI spicy://f/…. O token é usado só nesse download e nunca é armazenado.
Importar um LoRA de um repositório privado do Hugging Face
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:

  1. Alcançabilidade. Uma requisição HEAD em 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.
  2. 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.
  3. 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õe output_format, esse campo escolhe a codificação; quando não expõe, a codificação é própria do endpoint e mime é o único jeito de saber o que você recebeu.
  • Um arquivo ainda em cópia traz pending: true e nenhuma url. 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 field

Um 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 4

Repasse 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:

MensagemO que significaO que fazer
X: is requiredX está na lista required da raizAcrescente. Em endpoints de edição costuma ser prompt ou o campo de imagem
X: is not a supported fieldX não está em properties; campos desconhecidos são recusados, nunca ignoradosRemova, ou use a grafia deste endpoint — veja Tamanho da saída
X: must be one of: …O valor está fora de enumA mensagem lista os valores aceitos; enum e default estão no esquema
X: must contain at least N itemsAbaixo de minItemsUm array que o endpoint exige não pode ser enviado vazio
X: must contain at most N itemsAcima de maxItemsLeia o teto deste endpoint; ele não é igual em todos
X: must be an arrayO valor não tem o tipo declaradoColoque a entrada única dentro de uma lista
X: must be at most N charactersAcima de maxLengthPrompts também têm teto por endpoint
X.<n>.<sub>: is requiredUm subcampo da entrada <n> é obrigatório e não tem padrãoAcrescente só naquela entrada
X.<n>.<sub>: is not a supported fieldO esquema da entrada define additionalProperties: falseTire essa chave daquela entrada
X.<n>.<sub>: must be at most NO subcampo saiu da faixa deleLimite; 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

Nesta página