Texto, visão, imagem, voz, vídeo e busca atrás de um único endpoint, com um formato que você já conhece: Chat Completions. Você fala com a Entelecy; a Entelecy fala com os fornecedores, cuida das chaves, valida a requisição antes de gastar e cobra em créditos por uso real.
A API da Entelecy é um gateway. Ela recebe requisições no formato de mercado, aplica autenticação, validação e medição, encaminha para o fornecedor certo e devolve a resposta praticamente intacta — inclusive o streaming, chunk a chunk. Na prática, se o seu código já fala com uma API de LLM, trocar a base URL e a chave costuma bastar.
O que o gateway acrescenta ao caminho:
Uma credencial só. Nenhuma chave de fornecedor circula no cliente — elas ficam no servidor, e o que você guarda é uma chave kriou_live_… da Entelecy.
Validação antes do gasto. Tamanho de imagem, duração de vídeo, teto de caracteres e modelo habilitado são conferidos no gateway; erro de envelope volta em milissegundos, sem custo.
Medição no fim da chamada. O consumo real (tokens, segundos, caracteres, chamadas) vira débito em créditos na carteira da chave que fez a chamada.
Troca de fornecedor sem quebrar contrato. O modelo por trás de cada capacidade pode mudar; o formato da requisição não muda com ele.
A raiz é pública e serve como sonda de saúde: responde sem autenticação e diz qual build está no ar.
Toda chamada exige o header Authorization com uma chave da Entelecy no esquema Bearer. A chave nasce e vive na sua conta; o gateway a valida contra o Entelecy Account e mantém o resultado em cache curto, de modo que o custo dessa checagem é desprezível no caminho quente.
http
POST /v1/chat/completions HTTP/1.1
Host: api.entelecy.ai
Authorization: Bearer kriou_live_7Qb3xk9_M2pN-VtR4sLu8Z
Content-Type: application/json
Sobre as credenciais aceitas:
kriou_live_… — chave de produção. É a forma normal de integrar.
kriou_test_… — chave de teste, útil para ambientes de homologação. Endpoints podem ser configurados para recusá-la.
Atenção
Chave fora do formato, revogada ou expirada devolve 401 sem detalhar o motivo. A chave nunca aparece em log — nem inteira, nem parcial. Se ela vazar, revogue na conta: a validação em cache expira em segundos.
Convenções
Corpo e resposta em JSON UTF-8, salvo onde indicado (upload de áudio e mídia usa multipart/form-data; síntese de voz responde bytes de áudio; transcrição responde texto puro). Campos que o gateway não conhece são repassados ao fornecedor sem alteração — é o que permite usar recursos novos antes de eles aparecerem nesta página.
Headers
Header
Onde
Descrição
Authorization
requisição
Bearer + chave da Entelecy. Obrigatório em todos os endpoints, menos na raiz.
Content-Type
requisição
application/json, ou multipart/form-data nos endpoints de upload.
X-Image-Provider
requisição
Escolhe o fornecedor de imagem quando há mais de um habilitado. Opcional; existe um padrão de servidor.
Streaming
Com "stream": true a resposta vira text/event-stream: linhas data: …, uma por evento, sem buffer intermediário. O último evento útil carrega o consumo da chamada; depois dele vem data: [DONE].
O gateway sempre pede o bloco de uso ao fornecedor, então o chunk final com usage chega mesmo que você não o solicite. Se o stream terminar sem [DONE], trate como resposta truncada e repita a chamada — a conexão foi cortada no meio.
Erros
Erros gerados pelo gateway têm sempre a mesma forma: um objeto error com type, message e, quando ajuda, code e param. Erros vindos do fornecedor são repassados com o status e o corpo originais.
Corpo vazio, JSON inválido, campo obrigatório ausente ou envelope recusado por uma das regras do gateway.
401
—
Header ausente, chave fora do formato, revogada ou de ambiente não permitido.
402
insufficient_credits
Saldo abaixo do mínimo da operação. O corpo traz saldo, quanto falta e a URL para recarregar.
4xx / 5xx
repassado
Erro do fornecedor (limite de taxa, conteúdo recusado, indisponibilidade). Status e corpo chegam como vieram.
503
upstream_unavailable
O fornecedor caiu antes do primeiro byte e as retentativas do gateway se esgotaram. Em streaming, chega como evento SSE.
Códigos do gateway
São as recusas decididas antes de chamar o fornecedor. Todas voltam em 400, custam zero crédito e trazem mensagem acionável — dá para corrigir o pedido e repetir sem adivinhação.
code
Descrição
gateway_invalid_size
size fora do envelope do modelo: formato, múltiplo de pixels, área mínima ou máxima, aresta ou proporção.
gateway_unsupported_background
Fundo transparente pedido a um modelo que não suporta e sem alternativa configurada.
gateway_unknown_model
Modelo não habilitado para o endpoint. A mensagem lista os aceitos.
gateway_invalid_duration
duration ausente, não inteira ou fora da faixa permitida para vídeo.
gateway_invalid_resolution
Resolução de vídeo fora da lista aceita.
gateway_invalid_input
Texto acima do teto de caracteres da síntese de voz. Divida em partes.
Doze rotas, agrupadas por capacidade. A coluna da direita adianta a unidade de cobrança — o detalhe está em Cobrança.
Endpoint
Descrição
Unidade cobrada
POST /v1/chat/completions
Conversa e geração de texto no formato Chat Completions.
tokens
POST /v1/images/generations
Geração de imagem a partir de texto.
tokens
POST /v1/images/edits
Edição de imagem com máscara opcional.
tokens
POST /v1/audio/transcriptions
Transcrição de áudio (fala → texto).
segundos de áudio
POST /v1/audio/speech
Síntese de voz (texto → fala).
caracteres
GET /v1/audio/voices
Catálogo de vozes disponíveis.
sem cobrança
POST /v1/videos/generations
Geração de vídeo (submissão).
segundos gerados
GET /v1/videos/generations/{id}
Consulta do vídeo em processamento.
sem cobrança
POST /v1/videos/uploads
Upload de mídia de referência para vídeo a partir de imagem.
sem cobrança
POST /v1/search
Busca na web com resultados estruturados.
por chamada
GET /
Estado do serviço. Anônimo.
sem cobrança
Texto — Chat Completions
Formato compatível com Chat Completions: o mesmo corpo que você já manda para clientes OpenAI-compatíveis funciona aqui, incluindo ferramentas, saída estruturada e streaming. É o endpoint de uso geral — conversa, geração, extração, classificação, agentes.
POST/v1/chat/completionssuporta streaming (SSE)
Campos que o gateway lê ou trata. Os demais seguem para o fornecedor sem alteração.
Campo
Tipo
Descrição
modelobrigatório
string
Nome público do modelo: loom-flash ou loom-pro. Id de fornecedor é recusado com 400 — a mensagem lista os aceitos.
effort
string
Quanto o modelo deve pensar: none, high ou max. É parâmetro do gateway — traduzido e removido antes de encaminhar. Omitido, nada é reescrito.
messagesobrigatório
array
Turnos da conversa, com role (system, user, assistant, tool) e content.
stream
boolean
true troca a resposta por um stream SSE. Padrão false.
max_tokens
integer
Teto de tokens gerados na resposta.
temperature
number
Aleatoriedade da amostragem, quando o modelo aceita.
tools / tool_choice
array / object
Definição de ferramentas e política de escolha, no formato de mercado.
response_format
object
Saída estruturada — por exemplo, { "type": "json_object" }.
thinking
object
Campo do fornecedor. Idem: só é reescrito quando você manda effort.
reasoning_effort
string
Campo do fornecedor. Só é tocado se você mandar effort; sem ele, passa como veio.
Modelo e esforço
São dois eixos independentes, ambos no corpo: model escolhe a faixa e effort escolhe quanto o modelo pensa. Separados de propósito — cada combinação é uma linha de configuração no gateway, não um número mágico. O gateway traduz os dois para o vocabulário do fornecedor e remove o effort antes de encaminhar.
model
Perfil
Quando usar
loom-flash
Rápido e barato, para volume
Resposta curta, classificação, autocomplete, extração — onde a latência manda.
loom-pro
Mais capacidade, para trabalho difícil
Código, análise em várias etapas, redação longa, agentes com ferramentas.
effort
Perfil
Quando usar
none
Sem raciocínio explícito
O mais rápido e barato. Responde direto, sem etapa de pensamento.
high
Raciocínio ligado
O padrão recomendado quando a resposta precisa estar certa, não só rápida.
max
Raciocínio no teto
Problemas difíceis: refatoração grande, planejamento longo, correção acima de custo.
Nota
Sem effort no corpo, o gateway não toca em thinking nem em reasoning_effort — quem já manda esses campos na mão continua funcionando igual. E a resposta traz de volta o mesmo nome que você pediu em model (loom-flash, não o id do fornecedor): o que você manda e o que volta falam a mesma língua.
Requisição
curl
curl -N https://api.entelecy.ai/v1/chat/completions \
-H "Authorization: Bearer $ENTELECY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "loom-flash",
"effort": "high",
"stream": true,
"messages": [
{ "role": "system", "content": "Responda em portugues do Brasil, direto ao ponto." },
{ "role": "user", "content": "Resuma o conceito de entelequia em tres linhas." }
]
}'
Formato Chat Completions. O gateway normaliza a resposta do fornecedor antes de devolver: model volta com o nome público que você pediu e campos internos do fornecedor não passam. Campos que o fornecedor acrescente e o gateway ainda não conheça são repassados — documentado aqui está o que é estável.
Campo
Tipo
Descrição
id
string
Identificador único desta chamada. Opaco: não derive nada do formato.
object
string
chat.completion na resposta inteira; chat.completion.chunk em cada evento do stream.
created
integer
Momento da criação, em segundos Unix (UTC).
model
string
O nome público que você pediu (loom-flash). Não o id do fornecedor.
choices
array
Lista de respostas. Com n ausente, vem uma só.
choices[].index
integer
Posição desta escolha na lista.
choices[].message / delta
object
A mensagem gerada: role (assistant) e content. No stream este campo se chama delta e traz o pedaço novo, não o texto inteiro.
….reasoning
string
O raciocínio, quando o modelo pensa antes de responder. É texto de diagnóstico, não resposta: não mostre no lugar do content, e não conte com idioma nem formato.
….reasoning_content
string
Nome antigo do mesmo conteúdo, mantido em paralelo enquanto os clientes migram. Vai sair; use reasoning.
choices[].finish_reason
string
Por que parou: stop (fim natural), length (bateu o teto de tokens), tool_calls (quer chamar uma ferramenta).
usage
object
O consumo da chamada — é daqui que sai a cobrança. No stream vem no último evento, com choices vazio.
usage.prompt_tokens
integer
Tokens de entrada (o que você mandou).
usage.completion_tokens
integer
Tokens de saída (o que o modelo gerou), raciocínio incluído.
usage.total_tokens
integer
Soma dos dois. É o número que a cobrança usa.
…prompt_tokens_details.cached_tokens
integer
A parte da entrada que bateu no cache de prompt. Repetir um prefixo grande sai bem mais barato que reenviá-lo.
…completion_tokens_details.reasoning_tokens
integer
Quanto da saída foi raciocínio. Já está dentro de completion_tokens — não some.
O bloco usage é a base da cobrança: entrada, saída e a parte da entrada que bateu no cache de prompt — repetir um prefixo grande sai muito mais barato do que reenviá-lo do zero.
Imagem
Geração e edição de imagem no formato de mercado, com validação de envelope no gateway. O retorno traz a imagem em base64 e o consumo em tokens da chamada.
POST/v1/images/generations
Campo
Tipo
Descrição
modelobrigatório
string
Nome público do modelo de imagem: loom-image. Ver Modelos.
promptobrigatório
string
Descrição do que gerar. Prompts específicos rendem mais que adjetivos empilhados.
size
string
LARGURAxALTURA em pixels, ou auto. Validado antes do envio.
quality
string
Nível de qualidade aceito pelo modelo.
background
string
auto, opaque ou transparent.
n
integer
Quantidade de imagens.
output_format
string
Formato do arquivo devolvido, quando o modelo aceita escolher.
Envelope validado no gateway
O que o modelo aceita é conhecimento do gateway, não do seu código. Antes de chamar o fornecedor, a requisição passa por estas regras:
Os dois lados precisam ser múltiplos do passo de pixels do modelo.
A área total precisa ficar entre o mínimo e o máximo do modelo, e a maior aresta abaixo do teto.
A proporção entre lados não pode passar do limite do modelo.
Fundo transparente pedido a um modelo sem suporte é reencaminhado ao modelo alternativo — e a cobrança segue o modelo efetivamente usado.
Requisição
curl
curl https://api.entelecy.ai/v1/images/generations \
-H "Authorization: Bearer $ENTELECY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "loom-image",
"prompt": "Fachada de uma padaria de bairro ao amanhecer, luz quente, fotografia",
"size": "1536x1024",
"quality": "high",
"n": 1
}'
A edição recebe JSON: a imagem-base (uma ou mais) e a máscara opcional em base64. O gateway converte para o formato multipart que o fornecedor espera, então você não precisa montar o upload.
json
{
"model": "loom-image",
"prompt": "Troque o fundo por um ceu limpo no fim da tarde",
"image": ["iVBORw0KGgo…"],
"mask": "iVBORw0KGgo…",
"size": "1024x1024"
}
Nota
A máscara marca o que pode mudar: a área transparente é a editável. Envie máscara e imagem-base com as mesmas dimensões.
Áudio
Dois caminhos: transcrever fala em texto e sintetizar texto em voz. A escolha do motor de transcrição é feita pelo campo model; sem ele, vale o padrão do servidor.
POST/v1/audio/transcriptionsmultipart/form-data
Campo
Tipo
Descrição
fileobrigatório
file
Arquivo de áudio. Formatos usuais (m4a, mp3, wav, ogg, webm).
model
string
Nome público do motor de transcrição: loom-audio — o mesmo nome da voz; a rota é que diz se é fala→texto ou texto→fala.
language
string
Dica de idioma em ISO-639 (pt, en). Omitido, o idioma é detectado.
A resposta é texto puro (text/plain), não JSON — é o contrato que os clientes da Entelecy já consomem. A duração do áudio, medida na transcrição, é a base da cobrança.
curl
curl https://api.entelecy.ai/v1/audio/transcriptions \
-H "Authorization: Bearer $ENTELECY_API_KEY" \
-F "[email protected]" \
-F "model=loom-audio" \
-F "language=pt"
# 200 OK · text/plain
Bom dia. Comecando a reuniao de quinta…
POST/v1/audio/speechresponde bytes de áudio
Campo
Tipo
Descrição
inputobrigatório
string
Texto a ser falado. Há um teto de caracteres por requisição; acima dele, o gateway recusa antes de gastar.
voice
string
Identificador da voz. Consulte o catálogo em /v1/audio/voices.
model
string
Nome público do modelo de voz: loom-audio. Ver Modelos.
response_format
string
mp3 ou um formato nativo como mp3_44100_128, opus_48000_64, pcm_24000.
voice_settings
object
Ajustes finos da voz (estabilidade, semelhança, estilo), repassados ao fornecedor.
curl
curl https://api.entelecy.ai/v1/audio/speech \
-H "Authorization: Bearer $ENTELECY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "loom-audio",
"input": "A entrega de quinta esta confirmada. Qualquer mudanca, aviso por aqui.",
"response_format": "mp3_44100_128"
}' \
--output aviso.mp3
GET/v1/audio/voicessem cobrança
Lista as vozes disponíveis na conta, incluindo as prontas do fornecedor. Exige autenticação, não gera cobrança e é a origem correta dos identificadores usados em voice.
Vídeo
Geração de vídeo é assíncrona: você submete o pedido, recebe um identificador e consulta até o estado ficar terminal. Vídeo a partir de imagem usa uma mídia de referência enviada antes.
Nome público do modelo de vídeo: loom-video. Vale para texto→vídeo e para imagem→vídeo — o que muda é a mídia de referência.
promptobrigatório
string
Descrição da cena, do movimento de câmera e do ritmo.
durationobrigatório
integer
Duração em segundos, dentro da faixa permitida. É a base da cobrança, por isso não aceita automático.
resolution
string
Resolução dentro da lista aceita.
curl
# 1) submit — devolve o id da predicao
curl https://api.entelecy.ai/v1/videos/generations \
-H "Authorization: Bearer $ENTELECY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "loom-video",
"prompt": "Plano aereo de uma feira livre ao amanhecer, camera avancando devagar",
"duration": 6,
"resolution": "720p"
}'
# { "data": { "id": "pred_01J8Z…", "status": "queued" } }
# 2) poll — ate status completed
curl https://api.entelecy.ai/v1/videos/generations/pred_01J8Z… \
-H "Authorization: Bearer $ENTELECY_API_KEY"
# { "data": { "status": "completed", "outputs": [{ "url": "https://…/video.mp4" }] } }
Nota
A cobrança é armada na submissão e disparada na primeira consulta que vê o vídeo pronto. Consultar várias vezes não cobra em duplicidade; se o pedido falhar, não há débito.
POST/v1/videos/uploadsmultipart/form-data
Envia a imagem de referência para o fluxo imagem→vídeo e devolve o identificador que vai no corpo da geração. O upload não gera cobrança.
Busca
Busca na web com resultados estruturados — pensado para dar contexto atual a um agente antes de ele responder. O corpo é repassado ao mecanismo, então parâmetros de região, idioma e quantidade funcionam como na origem.
A resposta chega como veio do mecanismo: bloco de resposta direta quando existe, resultados orgânicos, painéis de conhecimento e afins. A cobrança é por chamada bem-sucedida, independentemente do número de resultados.
Modelos
O campo model recebe um nome público da Entelecy. Ele descreve a capacidade que você quer; qual modelo atende àquilo é decisão nossa, revisada continuamente. A resolução é por (nome, rota) — nome fora da lista da rota é 400 antes de qualquer gasto.
model
Endpoint
Descrição
loom-flash
/v1/chat/completions
Texto rápido e barato para volume. Aceita effort.
loom-pro
/v1/chat/completions
Texto com mais capacidade, para trabalho difícil. Aceita effort.
loom-image
/v1/images/generations
Geração de imagem. Fundo transparente é resolvido pelo gateway, sem você trocar de nome.
loom-audio
/v1/audio/speech
Síntese de voz com prosódia natural em português.
loom-audio
/v1/audio/transcriptions
Transcrição de áudio. Mesmo nome da voz: a rota decide a direção.
loom-video
/v1/videos/generations
Geração de vídeo curto, a partir de texto ou de uma imagem de referência.
Como escolhemos
Não somos fiéis a um fornecedor. Cada segmento é um campo em movimento — modelo novo todo mês, preço caindo, capacidade mudando — e o que faz sentido hoje pode não fazer no trimestre que vem. Acompanhamos as famílias relevantes de cada um, medimos no nosso próprio material e trocamos quando compensa.
O critério, em ordem:
Qualidade no português do Brasil. Avaliamos em conteúdo real de clientes, não em benchmark traduzido.
Custo por resultado aceito. Não o preço por token: o preço da resposta que passou na revisão.
Latência previsível. Modelo bom que oscila de 3 a 40 segundos não serve para produto interativo.
Estabilidade de contrato. Fornecedor que muda formato sem aviso custa caro no longo prazo.
Agentes
Raciocínio, código e ferramentas
Conversa, geração e extração de texto, leitura de imagens e documentos, e o ciclo de agente com ferramentas. É onde a diferença entre um modelo e outro mais aparece — e onde a escala de esforço rende mais.
Famílias acompanhadas no segmento
ClaudeGPTGeminiDeepSeekLlamaMistralQwenGrok
Imagem
Geração e edição
Peça de marca, cena fotográfica, ilustração, edição com máscara e fundo transparente. Avaliamos aderência ao prompt, tipografia legível dentro da imagem e consistência entre variações.
Narração longa, resposta curta interativa e leitura expressiva. O corte aqui é a naturalidade em português do Brasil — a maioria das opções ainda soa traduzida — junto com latência e controle de estilo.
Reunião, ditado, áudio de campo com ruído. Pesam acurácia em português, marcação de tempo por palavra, separação de interlocutores e o custo por hora de áudio.
Clipe curto para peça de conteúdo, animação de uma imagem existente, movimento de câmera dirigido. Coerência temporal, aderência ao prompt e custo por segundo mandam na escolha.
Famílias acompanhadas no segmento
VeoSoraSeedanceKlingRunwayLuma RayHailuoWan
Busca
Web com resultado estruturado
Contexto atual para um agente responder sem alucinar: resultado orgânico, resposta direta e painéis, em JSON limpo. Latência baixa importa mais que volume — o agente busca várias vezes por tarefa.
Isto é o panorama que acompanhamos em cada segmento, não um catálogo de disponibilidade. O que está ativo por trás de cada nome público é curadoria nossa e muda quando aparece coisa melhor — sem quebrar quem integrou, porque o nome não muda junto.
Cobrança
Tudo é cobrado em créditos, na carteira do dono da credencial. Cada capacidade tem a unidade que corresponde ao trabalho real:
Endpoint
Unidade cobrada
De onde sai a medida
/v1/chat/completions
tokens
usage da resposta, com entrada, saída e cache separados.
/v1/images/*
tokens
usage da resposta, incluindo os tokens de imagem gerados.
/v1/audio/transcriptions
segundos de áudio
Duração do áudio medida na transcrição, arredondada para cima.
/v1/audio/speech
caracteres
Quantidade de caracteres do texto enviado.
/v1/videos/generations
segundos gerados
Duração pedida na submissão, cobrada quando o vídeo fica pronto.
/v1/search
por chamada
Uma unidade por chamada bem-sucedida.
Antes de encaminhar, o gateway confere um saldo mínimo para a operação — maior em imagem e vídeo, que custam mais. Sem saldo, a resposta é 402 com quanto falta e o link para recarregar, e nada é gasto com o fornecedor.
O débito acontece depois da resposta, com o consumo real, e é idempotente por chamada: uma retentativa de rede não cobra duas vezes. Chamadas que falham no fornecedor não geram débito.
Atenção
Se o saldo acabar entre a checagem inicial e o débito, a resposta já foi entregue e o débito é registrado mesmo assim. É a única situação em que a carteira pode ficar negativa — a próxima chamada volta em 402.
Exemplos de implementação
Código pronto para colar, com os mesmos modelos que o Loom — nosso próprio produto — roda em produção, e com o tratamento que costuma faltar: erro de saldo, chunk partido no meio do stream e leitura do consumo no fim da chamada.
Chat com streaming
Sem dependência: fetch e TextDecoder nativos do Node 18+. O parser guarda a sobra do buffer porque um chunk de rede pode cortar uma linha SSE ao meio.
javascript
const BASE = 'https://api.entelecy.ai';
const KEY = process.env.ENTELECY_API_KEY;
/**
* Chat com streaming. Devolve o texto completo e o usage do ultimo chunk.
* O gateway sempre pede usage no fim do stream — nao e preciso configurar nada.
*/
export async function chatStream(messages, { effort = 'high', onDelta } = {}) {
const res = await fetch(`${BASE}/v1/chat/completions`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${KEY}`,
'Content-Type': 'application/json',
},
// model e effort sao eixos separados: o nome escolhe a faixa, o effort a profundidade
body: JSON.stringify({ model: 'loom-flash', effort, stream: true, messages }),
});
if (!res.ok) {
// 402 = saldo insuficiente; o corpo traz balance/required/upgrade_url
const err = await res.json().catch(() => ({}));
throw new Error(`${res.status} ${err?.error?.type ?? 'erro'}: ${err?.error?.message ?? ''}`);
}
const reader = res.body.getReader();
const decoder = new TextDecoder();
let buffer = '', text = '', usage = null;
while (true) {
const { value, done } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
// SSE: eventos separados por linha; so nos importam as linhas "data: "
const lines = buffer.split('\n');
buffer = lines.pop() ?? '';
for (const line of lines) {
if (!line.startsWith('data: ')) continue;
const payload = line.slice(6).trim();
if (payload === '[DONE]') continue;
let chunk;
try { chunk = JSON.parse(payload); } catch { continue; } // chunk partido
const delta = chunk.choices?.[0]?.delta?.content;
if (delta) { text += delta; onDelta?.(delta); }
if (chunk.usage) usage = chunk.usage; // ultimo chunk
}
}
return { text, usage };
}
Gerar imagem e salvar em disco
javascript
import { writeFile } from 'node:fs/promises';
export async function gerarImagem(prompt, { size = '1024x1024' } = {}) {
const res = await fetch(`${BASE}/v1/images/generations`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ model: 'loom-image', prompt, size, n: 1 }),
});
const body = await res.json();
if (!res.ok) {
// gateway_invalid_size chega aqui ANTES de custar credito
throw new Error(`${body.error?.code ?? res.status}: ${body.error?.message}`);
}
await writeFile('saida.png', Buffer.from(body.data[0].b64_json, 'base64'));
return body.usage;
}
Chat com streaming
Com requests. Note o timeout em par: conexão curta, leitura longa — geração com raciocínio pode passar de um minuto.
python
import json, os, requests
BASE = "https://api.entelecy.ai"
KEY = os.environ["ENTELECY_API_KEY"]
def chat_stream(messages, effort: str = "high"):
"""Chat com streaming. Retorna (texto, usage)."""
with requests.post(
f"{BASE}/v1/chat/completions",
headers={
"Authorization": f"Bearer {KEY}",
"Content-Type": "application/json",
},
json={"model": "loom-flash", "effort": effort, "stream": True, "messages": messages},
stream=True,
timeout=(10, 600), # conexao curta, leitura longa
) as res:
if res.status_code == 402:
raise RuntimeError(f"saldo insuficiente: {res.json()['error']}")
res.raise_for_status()
texto, usage = [], None
for raw in res.iter_lines(decode_unicode=True):
if not raw or not raw.startswith("data: "):
continue
payload = raw[6:].strip()
if payload == "[DONE]":
break
chunk = json.loads(payload)
if chunk.get("choices"):
delta = chunk["choices"][0].get("delta", {}).get("content")
if delta:
texto.append(delta)
print(delta, end="", flush=True)
if chunk.get("usage"):
usage = chunk["usage"]
return "".join(texto), usage
Com HttpClient e System.Text.Json, lendo o stream conforme ele chega em vez de esperar o corpo inteiro.
csharp
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
public sealed class EntelecyClient(HttpClient http, string apiKey)
{
private static readonly JsonSerializerOptions Json = new(JsonSerializerDefaults.Web);
/// <summary>Chat com streaming: entrega cada delta no callback e devolve o usage final.</summary>
public async Task<JsonElement?> ChatStreamAsync(
object[] messages, Action<string> onDelta, string effort = "high", CancellationToken ct = default)
{
var body = JsonSerializer.Serialize(new { model = "loom-flash", effort, stream = true, messages });
using var req = new HttpRequestMessage(HttpMethod.Post, "/v1/chat/completions")
{
Content = new StringContent(body, Encoding.UTF8, "application/json")
};
req.Headers.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);
using var res = await http.SendAsync(req, HttpCompletionOption.ResponseHeadersRead, ct);
if (!res.IsSuccessStatusCode)
throw new InvalidOperationException(
$"{(int)res.StatusCode}: {await res.Content.ReadAsStringAsync(ct)}");
await using var stream = await res.Content.ReadAsStreamAsync(ct);
using var reader = new StreamReader(stream);
JsonElement? usage = null;
while (await reader.ReadLineAsync(ct) is { } line)
{
if (!line.StartsWith("data: ", StringComparison.Ordinal)) continue;
var payload = line[6..].Trim();
if (payload == "[DONE]") break;
JsonDocument doc;
try { doc = JsonDocument.Parse(payload); } catch (JsonException) { continue; }
using (doc)
{
if (doc.RootElement.TryGetProperty("choices", out var choices)
&& choices.GetArrayLength() > 0
&& choices[0].TryGetProperty("delta", out var delta)
&& delta.TryGetProperty("content", out var content))
{
onDelta(content.GetString() ?? "");
}
if (doc.RootElement.TryGetProperty("usage", out var u))
usage = u.Clone();
}
}
return usage;
}
}
Limites e boas práticas
Use streaming em resposta longa. Além da percepção de velocidade, evita timeout de proxy em geração demorada.
Repita com recuo exponencial. Erros de limite de taxa e indisponibilidade do fornecedor são transitórios; erros 4xx do gateway não melhoram com retentativa.
Marque o prefixo estável com cache. Em prompts com base de conhecimento repetida, é a economia mais fácil de conquistar.
Valide tamanho de imagem no seu formulário. O gateway recusa de graça, mas a viagem até ele custa tempo do usuário.
Divida texto longo antes da síntese de voz. Há teto por requisição, e trechos menores ficam melhores de ouvir.
Guarde o identificador do vídeo. A consulta é a única forma de recuperar o resultado, e é ela que dispara a cobrança quando fica pronto.
Precisa de limite maior, endpoint dedicado ou modelo fora desta lista? Fale com a gente em [email protected].