Como gerar vídeos e imagens com IA pela API do Meta Muse

16 min de leitura • 12 de outubro de 2026 (30 de outubro de 2026)

A useapi.net é uma API de terceiros. Não tem afiliação com a Meta nem é endossada por ela.

Sumário

  1. Introdução
  2. Quanto custou
  3. Antes de começar
  4. Como foi feito
    1. 1. Adicionar a conta
    2. 2. Enviar as fotos
    3. 3. Imagens a partir de texto
    4. 4. Três fotos em uma imagem
    5. 5. A imagem vira vídeo, sem novo upload
    6. 6. Restaurar e colorizar uma foto antiga
    7. 7. O retrato ganha vida
    8. 8. Trocar a motociclista no vídeo
    9. 9. Conferir a cota semanal
  5. O que aprendemos
  6. Faça um Short narrado de 30 segundos (roteiro, três cenas, uma junção)
    1. 1. Escrever o roteiro em JSON
    2. 2. Três cenas ao mesmo tempo
    3. 3. Consultar os jobs
    4. 4. Juntar os clipes em um chat
    5. 5. Baixar o Short
    6. Uma voz fixa: texto para fala como audio_1
  7. Perguntas frequentes
  8. Conclusão

Introdução

A Muse API da useapi.net controla o Muse, o agente pessoal de IA da Meta, a partir do código: um POST gera uma imagem ou um vídeo de 10 segundos com diálogo falado, e cada arquivo gerado é um asset id que o próximo job pode usar como entrada.

O Muse aqui é o agente da Meta em muse.ai, não a Skiv, a empresa de hospedagem de vídeo que usou o nome muse.ai até março de 2026. A API é hospedada, então, ao contrário de ferramentas que você hospeda, como o muse2api, não há nada para implantar: a comparação de APIs de vídeo do Meta Muse (em inglês) põe a API da própria Meta, o muse2api e as APIs hospedadas lado a lado.

A API roda na sua assinatura do site Muse, e o plano gratuito funciona. Este passo a passo pega três fotos comuns, uma moto BMW antiga, uma motociclista ruiva e a praia embaixo da Golden Gate, e as transforma em uma imagem dela pilotando essa moto pela arrebentação. A imagem depois vira um vídeo em que ela grita uma fala, e, em um terceiro job, outra mulher, tirada de uma quarta foto, toma o lugar dela no vídeo enquanto o passeio continua igual. No caminho, ele gera imagens a partir de texto, e restaura e coloriza um retrato de cem anos que depois levanta os olhos e fala. Cada requisição abaixo é a que gerou a mídia desta página.

A motociclista de uma foto, colocada em um clipe que o Muse fez a partir de uma imagem parada (passo 8).

Quanto custou

A useapi.net é uma API, não um serviço próprio de imagem ou vídeo. Cada chamada roda em uma conta Muse que você adiciona pelo Setup Muse, e gasta a cota semanal do Muse dessa conta, sem cobrança por imagem ou por vídeo da useapi.net. O Muse informa a cota como um percentual inteiro usado, então uma única imagem muitas vezes não mostra mudança.

O que cada passo gastou no plano gratuito, tirado do percentUsed antes e depois dele, e quanto tempo cada job levou:

Passo Endpoint Saída Cota usada Tempo
Texto para imagem, 9:16 POST /jobs/image 1152×2048 não medido 30 s
Texto para imagem, exatamente 16:9 POST /jobs/image 2048×1152 0% 21 s
Motociclista + moto + praia POST /jobs/image 2048×1152 0% 31 s
O passeio como vídeo POST /jobs/video 1280×720, 10 s 3% 88 s
Restaurar uma foto antiga POST /jobs/image 1344×1792 1% 34 s
O retrato fala POST /jobs/video 832×1104, 10 s 2% 68 s
Trocar a motociclista POST /jobs/video 1248×704, 10 s 3% 138 s

Uploads não gastam cota. No plano gratuito, um vídeo gastou de 2 a 3 por cento da semana e uma imagem de 0 a 1 por cento, então uma conta gratuita gera mais ou menos 30 a 50 vídeos por semana, ou várias centenas de imagens (passo 9).

Antes de começar

Você precisa de um token da API da useapi.net e de uma conta Muse adicionada pelo Setup Muse. Antes de adicionar a conta:

  • O Muse está disponível nos EUA, no Canadá, no Japão e em Singapura (em 30 de outubro de 2026), então crie a conta em um desses lugares. Depois de adicionada, você pode chamar a API de qualquer lugar.
  • Termine o onboarding do Muse em muse.ai, incluindo a verificação de idade da Meta, e crie uma imagem lá.
  • A conta precisa ter login com e-mail e senha, porque a API entra com o e-mail e a senha. Uma conta Meta que só entra com um código enviado por e-mail ainda não tem senha: crie uma na Central de Contas da Meta (Senha e segurança) e depois adicione a conta aqui. Sem senha, a adição é recusada com 400.
  • Use a conta só para a API, sem outras ferramentas de automação nela. Abrir a conta em muse.ai no navegador não tem problema, mas só a partir dos EUA, do Canadá, do Japão ou de Singapura (as regiões em que o Muse tem suporte oficial), ou por uma VPN em uma delas: a Meta pode desligar o vídeo de uma conta que ela vê sendo usada de outro lugar.

Todas as chamadas também estão na coleção do Postman.

Sem código: o template n8n Old Photo to Talking Video restaura uma foto e faz ela falar a partir de um formulário, e o template Narrated Faceless Shorts transforma um tema em um Short narrado. O passo a passo inteiro também está no GitHub como um único script, em Node.js e Python: images-videos/ em useapi/muse-api, além de uma agent skill para Claude Code e Codex.

Os passos usam curl, jq e estas variáveis e helpers:

Abrir as variáveis e os helpers
export USEAPI_TOKEN="user:12345-..."
export EMAIL="[email protected]"
export MUSE_PASSWORD="..."   # a senha da conta Muse, usada uma vez no passo 1
API=https://api.useapi.net/v1/muse
AUTH="Authorization: Bearer $USEAPI_TOKEN"
enc() { jq -rn --arg v "$1" '$v|@uri'; }
post() { curl -sS -X POST "$API/$1" -H "$AUTH" -H "Content-Type: application/json" -d "$2"; }
get() { curl -sS "$API/$1" -H "$AUTH"; }
upload() { curl -sS -X POST "$API/assets?email=$(enc "$EMAIL")" -H "$AUTH" -H "Content-Type: $2" --data-binary @"$1" | jq -r .assetId; }
download() { curl -sSf "$API/assets/$(enc "$1")" -H "$AUTH" -o "$2"; }

# roda um job de forma assíncrona: envia, consulta GET /jobs/{jobid} a cada 10 s e salva o registro final do job em $1
run() {
  local job id
  job=$(post "$2" "$(jq '. + {async: true}' <<< "$3")")
  id=$(jq -r .jobid <<< "$job")
  while [ "$(jq -r .status <<< "$job")" = started ]; do
    sleep 10
    job=$(get "jobs/$(enc "$id")")
  done
  echo "$job" > "$1"
  jq '{status, error, text: .result.text, asset: .result.assets[0].assetId}' "$1"
}

Toda geração roda como um job. Sem async, um POST espera até cerca de 100 segundos e devolve o registro do job pronto, ou 202 com o job ainda rodando. Um vídeo aqui levou de 68 a 138 segundos, então o run envia todo job com async: true e consulta GET /jobs/jobid. Se o run mostrar qualquer status diferente de completed, leia o error antes de continuar.

Para receber um callback em vez de consultar, acrescente replyUrl: "https://example.com/hooks/muse" (e, se quiser, replyRef) a qualquer corpo abaixo. A API então envia um único POST do registro final do job, em JSON, para essa URL.

Os ids têm : e @, então o enc faz o URL-encode deles sempre que entram em um caminho de URL. Todas as entradas de um job precisam estar na mesma conta Muse, então os uploads fixam o email, e um job roda na conta que guarda as suas entradas.

Como foi feito

1. Adicionar a conta

POST /accounts recebe o e-mail e a senha da conta Muse. A API entra uma vez, guarda a sessão de 30 dias e renova sozinha. O Muse manda à conta um código de login por e-mail a cada entrada. Esse e-mail é só um aviso, não é preciso digitar nada.

curl — POST /accounts
post accounts "$(jq -n --arg email "$EMAIL" --arg password "$MUSE_PASSWORD" '{email: $email, password: $password}')" | jq '{health, maxJobs, quota}'

A resposta mostra health: "OK", maxJobs (10 por padrão) e o plano e o uso da conta, aqui tier: "HATCH_FREE", o plano gratuito. Se ela responder 596, a Meta quer uma verificação de segurança: entre uma vez em muse.ai em um navegador, conclua a verificação e mande o mesmo e-mail e senha de novo.

2. Enviar as fotos

POST /assets recebe o arquivo bruto como corpo, até 50 MB, e grava no workspace do Muse da conta. Nada fica guardado na useapi.net. O assetId da resposta é o que um job recebe como image_N.

curl — POST /assets
BMW=$(upload bmw.jpeg image/jpeg)
RIDER=$(upload rider.jpeg image/jpeg)
BEACH=$(upload beach.jpeg image/jpeg)
OLD_PHOTO=$(upload old-photo.jpeg image/jpeg)
PINK=$(upload pink-hair.jpeg image/jpeg)
echo "$BMW"
# user:12345-email:6578616d706c6540676d61696c2e636f6d-image:7db53e7a1f0979cbab6ff46ec3f0a35b60e6ee579a0d738a9628cf46a0c7a0f5.jpg
<code>image_1</code> — a moto
image_1 — a moto
<code>image_2</code> — a motociclista
image_2 — a motociclista
<code>image_3</code> — a praia
image_3 — a praia
o retrato antigo
o retrato antigo
a segunda motociclista
a segunda motociclista

As cinco entradas: a moto, a motociclista, a praia, o retrato antigo e a segunda motociclista.

O asset id é montado a partir do conteúdo do arquivo, então enviar o mesmo arquivo de novo para a mesma conta devolve o mesmo id e não grava nada.

3. Imagens a partir de texto

O Muse não tem parâmetro de proporção: quem diz é o prompt. Primeiro, um retrato vertical:

curl — POST /jobs/image
run portrait.json jobs/image "$(jq -n '{prompt: "Vertical 9:16 full-body portrait photo: a confident young woman biker with long wavy fiery red hair, green eyes and freckles, in a worn black leather jacket, dark jeans and boots, standing in a rain-soaked neon alley at dusk, looking straight at the camera, cinematic lighting, photorealistic."}')"
download "$(jq -r '.result.assets[0].assetId' portrait.json)" portrait.webp

Texto para imagem em 9:16

abrir em nova aba

Essa voltou exatamente em 9:16, 1152×2048. Uma versão em paisagem pedida como Cinematic 16:9 landscape photo: … voltou em 1920×1280, que é 3:2. Escrever a proporção como in exactly 16:9 aspect ratio resolveu:

curl — POST /jobs/image
run bmw-noir.json jobs/image "$(jq -n '{prompt: "In exactly 16:9 aspect ratio (landscape), a cinematic photo of a vintage 1920s black BMW motorcycle with white pinstripes, spoked wheels and a brown leather saddle, parked on wet cobblestones at night in San Francisco, the Golden Gate Bridge glowing through fog in the distance, neo-noir moonlight, reflections on the wet street, shot on 35mm film."}')"
download "$(jq -r '.result.assets[0].assetId' bmw-noir.json)" bmw-noir.webp

Texto para imagem em exatamente 16:9

abrir em nova aba

O Muse respondeu “That 16:9 frame with the bridge ghosting through the fog is pure neo-noir. Enjoy.” A resposta dele fica em result.text.

4. Três fotos em uma imagem

Até 9 imagens entram em um job como image_1..image_9, e o prompt dá nome a elas. Aqui a moto é image_1, a motociclista image_2 e a praia image_3. O prompt pede ao Muse que use a praia fotografada do jeito que está, então a Golden Gate fica onde estava na foto.

curl — POST /jobs/image
run beach-ride.json jobs/image "$(jq -n --arg bmw "$BMW" --arg rider "$RIDER" --arg beach "$BEACH" \
  '{image_1: $bmw, image_2: $rider, image_3: $beach,
  prompt: "In exactly 16:9 aspect ratio (landscape), a cinematic photo: the woman from image_2 (her face, long wavy red hair, light blue top and jeans) riding the exact vintage motorcycle from image_1 along the shoreline of the beach in image_3, the front wheel cutting through the shallow surf and throwing spray, bright afternoon sun, her hair streaming in the wind, photorealistic. Use the beach, rocks and Golden Gate Bridge from image_3 as the background exactly as photographed: same position, same orientation, not mirrored or redrawn. Keep her face and the motorcycle'\''s design exactly as in the images."}')"
BEACH_RIDE=$(jq -r '.result.assets[0].assetId' beach-ride.json)
download "$BEACH_RIDE" beach-ride.webp

A motociclista, a moto e a praia em uma imagem

abrir em nova aba

31 segundos, e nenhuma mudança visível na cota semanal.

5. A imagem vira vídeo, sem novo upload

BEACH_RIDE é o id de um arquivo que o Muse gerou. Ele já está no workspace da conta, então entra direto no próximo job como image_1, sem download e sem upload. Animate image_1 faz um clipe que começa na imagem e mantém o formato dela, e a fala entre aspas é dita.

curl — POST /jobs/video
run surf.json jobs/video "$(jq -n --arg img "$BEACH_RIDE" '{image_1: $img,
  prompt: "Animate image_1 into a cinematic video in exactly 16:9 aspect ratio (landscape): she rides the vintage motorcycle along the shoreline, the front wheel slices through the shallow waves and sends spray flying, the old engine roars, she raises one fist and shouts with a laugh: \"Nothing beats this view!\" The camera tracks alongside her. The beach, the rocks and the Golden Gate Bridge stay exactly as in image_1 (same orientation). Keep her face and the motorcycle exactly as in image_1."}')"
SURF=$(jq -r '.result.assets[0].assetId' surf.json)
download "$SURF" surf.mp4

Também no YouTube: 3 photos in, 1 talking video out: Meta Muse API from REST.

10 segundos em 1280×720, com som. O Muse disse “Done. 10 seconds, 16:9 landscape, straight from your image — no edits, no upscaling.”

A primeira tentativa deste mesmo job falhou depois de 37 segundos: o Muse respondeu que “the video worker glitched out before it rendered anything, so no clip was made”. A API informa uma resposta assim como 503, nada gerado, e a mesma requisição funcionou quando rodou de novo. Uma recusa por conteúdo vem como 403 e precisa de outro prompt.

6. Restaurar e colorizar uma foto antiga

Um job de imagem também pode consertar uma imagem. O retrato antigo é uma foto sépia desbotada de 685×900 pixels, 3:4. O prompt lista o que corrigir e insiste no mesmo enquadramento.

curl — POST /jobs/image
run restored.json jobs/image "$(jq -n --arg img "$OLD_PHOTO" '{image_1: $img,
  prompt: "Restore the old photograph image_1 as a high-quality modern photo: remove the scratches, dust, stains, fading and grain, recover sharp detail in the face, hair and fabric, and colorize it naturally (warm natural skin tones, dark brown hair, ivory tulle shawl, a soft pink rose with green leaves, a muted warm background). Keep the exact same person, expression, pose, framing and crop, and the same portrait aspect ratio as image_1 (3:4, 685×900): do not crop, extend or reframe it."}')"
RESTORED=$(jq -r '.result.assets[0].assetId' restored.json)
download "$RESTORED" restored.webp
Antes: o original, 685×900
Antes: o original, 685×900
Depois: restaurada e colorizada, 1344×1792
Depois: restaurada e colorizada, 1344×1792

O resultado tem 1344×1792, o mesmo enquadramento 3:4 com cerca do dobro do tamanho, com a mesma pose e o mesmo corte. O Muse disse “Done — restored, de-grained, and colorized, same framing and pose.”

7. O retrato ganha vida

A imagem restaurada entra direto em um job de vídeo. O clipe mantém o formato do retrato, então voltou em 832×1104.

curl — POST /jobs/video
run portrait-video.json jobs/video "$(jq -n --arg img "$RESTORED" '{image_1: $img,
  prompt: "Animate image_1 into a video with the same portrait frame: the young woman slowly lifts her eyes to the camera, her smile widens, she gently touches the rose on her shoulder and says softly: \"It has been a hundred years... and you still remember me.\" A light breeze moves a few strands of her hair, soft window light, the camera stays still with a very slow push-in. Keep her face exactly as in image_1."}')"
download "$(jq -r '.result.assets[0].assetId' portrait-video.json)" portrait.mp4

Também no YouTube: This old portrait just spoke: one API call (Meta Muse API, third-party).

8. Trocar a motociclista no vídeo

Um job de vídeo também aceita vídeos. video_1 é o clipe na arrebentação do passo 5, e image_1 é a foto de uma segunda mulher, de cabelo rosa preso em dois coques. O prompt pede ao Muse que troque a motociclista e mantenha todo o resto, e dá a ela uma fala nova.

curl — POST /jobs/video
run swap.json jobs/video "$(jq -n --arg vid "$SURF" --arg img "$PINK" '{video_1: $vid, image_1: $img,
  prompt: "Edit video_1: replace the rider with the woman from image_1 (her face, pink hair in two buns, colorful makeup and tattoos), and keep everything else the same: the motorcycle, the beach, the waves and spray, the Golden Gate Bridge, the light, the camera move and the timing. Instead of the original line she shouts: \"Catch me if you can, San Francisco!\" Keep the 16:9 frame and the same length."}')"
download "$(jq -r '.result.assets[0].assetId' swap.json)" swap.mp4
Antes: a motociclista original
Depois: a motociclista da foto

Com 138 segundos, este foi o job mais lento do passo a passo, e é por isso que todo job aqui roda de forma assíncrona. O clipe é o mesmo passeio, com a outra mulher na moto, gritando a fala nova. O Muse disse “Done. She’s in, 10 seconds, 16:9, same ride, same bridge, same waves.”

Também no YouTube: Replace the person in a video, keep everything else: Meta Muse API.

9. Conferir a cota semanal

GET /accounts/email lê o plano e o uso da conta ao vivo:

curl — GET /accounts/email
get "accounts/$(enc "$EMAIL")" | jq '{session, quota}'

quota.percentUsed é a parte da semana já usada, em percentual inteiro, e quota.resetsAt é quando ela recomeça, uma semana depois do início. Os três vídeos acima gastaram de 2 a 3 por cento cada e as imagens 0 ou 1. Na sessão de testes inteira, que também rodou dois jobs que não aparecem aqui, a conta gratuita foi de 16 a 28 por cento. Quando uma conta esgota, os novos jobs sem email vão para as suas outras contas, e um job fixado nela responde 412 até resetsAt.

O que aprendemos

  • Escreva a proporção como in exactly 16:9 aspect ratio. Cinematic 16:9 landscape photo voltou em 3:2 uma vez.
  • Todo vídeo tem cerca de 10 segundos. Um prompt pedindo 5 segundos ainda recebeu 10.
  • Um clipe de imagem para vídeo mantém o formato da imagem, a menos que o prompt peça outro: in exactly 9:16 aspect ratio transformou a nossa imagem da praia em 16:9 em um clipe vertical de 720×1280, reenquadrado em vez de esticado.
  • Falas escritas entre aspas em um prompt de vídeo são ditas.
  • Passe os arquivos gerados adiante pelo assetId. Eles ficam no workspace da conta, então uma cadeia de edições não precisa de downloads no meio.
  • Diga o que tem que continuar igual. “Same position, same orientation, not mirrored or redrawn” manteve a praia real na composição.
  • Vale rodar de novo um 503 com palavras do próprio Muse, como “glitched out”. Um 403 é uma recusa e precisa de outro prompt.
  • Mande vídeos com async: true. Uma requisição síncrona espera cerca de 100 segundos, e a troca levou 138.

Faça um Short narrado de 30 segundos (roteiro, três cenas, uma junção)

Um vídeo do Muse tem sempre cerca de 10 segundos, então um Short vertical de 30 segundos com narrador leva três jobs de vídeo e dois chats. Um chat escreve o roteiro em JSON, três jobs de vídeo rodam lado a lado, um por cena, cada um dizendo a sua fala na voz do mesmo narrador, e um último chat junta os clipes com ffmpeg no próprio workspace da conta. O Short abaixo, sobre a erupção do Monte Tambora em 1815 e o “ano sem verão” que veio depois, foi de um tema de uma linha a um MP4 em cerca de 2 minutos.

Também no YouTube: A narrated 30-second Short made only with Meta Muse API calls.

Três clipes de 10 segundos juntados em um Short de 720×1280 com 30 segundos, a partir do tema “how a single volcano cooled the whole planet in 1816”.

O roteiro levou 11 segundos, os três clipes 75, 75 e 87 segundos rodando ao mesmo tempo, e a junção 20 segundos, então um Short leva cerca de 2 a 3 minutos. Este foi feito em um plano pago do Muse. Nele, os quatro vídeos daquela sessão de testes (estes três e mais um) mexeram a leitura semanal em 2 pontos no total, e os chats em nenhum. No plano gratuito, um vídeo gasta de 2 a 3 por cento da semana, então um Short de três cenas custa lá cerca de 6 a 9 por cento.

Sem código: o mesmo fluxo é um app n8n, o template Narrated Faceless Shorts, com formulário, página de roteiro e salvamento no Google Drive. Dois Shorts que ele fez, publicados do jeito que saíram: The Great Pyramid was once bright white e Why honey never spoils, que ele narrou com o texto para fala do Muse.

O fluxo inteiro também é um único script, em Node.js e Python: narrated-short/ em useapi/muse-api. Ele recebe um tema e, se você quiser, o número de cenas e a sua própria descrição de narrador, e salva o Short em output/.

Os passos usam os helpers acima mais o chat, que inicia um chat e lê GET /chats/chatId até o Muse terminar:

Abrir o helper de chat
# inicia um chat, consulta GET /chats/{chatId} a cada 8 s até o Muse terminar e salva o chat em $1
chat() {
  local id c
  id=$(post chats "$2" | jq -r .chatId)
  while sleep 8; do
    c=$(get "chats/$(enc "$id")")
    jq -e '.pendingApprovals | length > 0' <<< "$c" > /dev/null &&
      echo "Muse asks for permission: answer it with POST /chats/{chatId}/approvals/{approvalId}"
    jq -e '(.status | IN("completed", "interrupted", "failed")) and (.pendingApprovals | length == 0)' <<< "$c" > /dev/null && break
  done
  echo "$c" > "$1"
  jq '{status, reply: ([.messages[] | select(.role == "assistant" and .text != "")][-1].text)}' "$1"
}

Toda requisição abaixo fixa o email. Um chat que recebe arquivos precisa rodar na conta que guarda esses arquivos, então os três clipes e a junção ficam todos em uma conta.

1. Escrever o roteiro em JSON

O primeiro chat pede exatamente três cenas e descreve o formato do JSON. Cada cena tem uma line, o que o narrador diz, limitada a 25 palavras para caber em 10 segundos, e um visual, uma frase que descreve a tomada. O prompt também diz ao Muse para não criar arquivos, então o chat fica só em texto.

curl — POST /chats
TOPIC="how a single volcano cooled the whole planet in 1816"
chat script.json "$(jq -n --arg email "$EMAIL" --arg topic "$TOPIC" '{email: $email,
  message: "Write a script for a 30-second faceless YouTube Short about: \"\($topic)\". Exactly 3 scenes. Reply with ONLY a JSON object, no prose, no markdown code fences, in this exact shape: {\"title\": string, \"scenes\": [{\"line\": string, \"visual\": string}, {\"line\": string, \"visual\": string}, {\"line\": string, \"visual\": string}]}. \"line\" is what the off-screen narrator says (at most 25 words, about 9 seconds spoken). \"visual\" is a one-sentence description of the 10-second shot, no people talking on camera. Do not create any files, images or videos."}')"
jq -r '[.messages[] | select(.role == "assistant" and .text != "")][-1].text' script.json | sed '/^```/d' > scenes.json
jq . scenes.json

O Muse respondeu em 11 segundos só com o JSON:

{
  "title": "How One Volcano Cooled the Whole Planet in 1816",
  "scenes": [
    {
      "line": "In April 1815, Mount Tambora in Indonesia erupted, the largest explosion in recorded history, blasting ash forty kilometers into the sky.",
      "visual": "A massive volcanic eruption at dawn, with a towering ash column rising into the stratosphere above a dark island."
    },
    {
      "line": "That ash veil circled Earth, blocking sunlight and dropping global temperatures, turning summer into winter across North America and Europe.",
      "visual": "An animated globe wrapped in a gray haze as sunlight dims and frost spreads across continents."
    },
    {
      "line": "1816 became the Year Without a Summer: crops failed, snow fell in July, and famine spread as the planet shivered.",
      "visual": "Snow falling over green farmland in summer, covering withered crops and empty village fields under a pale sun."
    }
  ]
}

Rodamos o mesmo prompt com três temas e recebemos JSON limpo todas as vezes, em 11 a 29 segundos, sem blocos de código e sem texto extra. É uma amostra pequena, então o sed tira mesmo assim uma linha solta de ```, e o script do GitHub pergunta de novo quando a resposta não é um JSON válido.

2. Três cenas ao mesmo tempo

Cada cena vira um POST /jobs/video assíncrono. Os três prompts levam a mesma descrição do narrador, palavra por palavra, seguida de The narrator says exactly: "<line>". O Muse diz uma fala entre aspas como está escrita, e a descrição em comum mantém a voz parecida de um clipe para outro. Vertical 9:16 video voltou em 720×1280.

curl — POST /jobs/video
NARRATOR="Narration: one off-screen narrator only, the same voice throughout: a calm, deep, middle-aged British male documentary narrator, measured pace, warm and authoritative, like a nature documentary. Nobody on screen speaks. No music."
for i in 0 1 2; do
  post jobs/video "$(jq --arg email "$EMAIL" --arg narrator "$NARRATOR" --argjson i $i '.scenes[$i] | {email: $email, async: true,
    prompt: "Vertical 9:16 video, about 10 seconds. \(.visual) Slow cinematic camera move, photoreal. \($narrator) The narrator says exactly: \"\(.line)\""}' scenes.json)" | jq -r .jobid
done > jobs.txt

Cada POST responde 201 na hora com um jobid. Os três jobs ocupam três das vagas de maxJobs da conta (10 por padrão), então rodam lado a lado. Um 429 aqui quer dizer que todas as vagas estão ocupadas e nada foi iniciado: mande esse de novo em 30 segundos.

3. Consultar os jobs

GET /jobs/jobid responde status: "started" até o clipe ficar pronto. Os jobs rodam em paralelo, então ler um depois do outro não acrescenta tempo:

curl — GET /jobs/jobid
for n in 1 2 3; do
  id=$(sed -n "${n}p" jobs.txt)
  until job=$(get "jobs/$(enc "$id")"); [ "$(jq -r .status <<< "$job")" != started ]; do sleep 10; done
  echo "$job" > clip$n.json
  jq '{status, error, text: .result.text, asset: .result.assets[0].assetId}' clip$n.json
done

Os três terminaram. Sobre o primeiro, o Muse disse “Here it is — vertical 9:16, photoreal volcanic eruption at dawn with the narration as specified”. Cada clipe diz a sua fala palavra por palavra, e todas as falas, com 20 a 21 palavras, couberam nos seus 10 segundos. Um job que falhou com 503 não gerou nada e pode rodar de novo. Qualquer outra falha, como uma recusa 403, pede uma olhada em error.message antes.

4. Juntar os clipes em um chat

Os três ids dos clipes entram em um chat como files, em que o Muse os vê como video_1, video_2 e video_3. A mensagem pede ffmpeg, a ordem, o áudio e um nome de arquivo, e termina com No public links. Com essa linha, o Muse não parou para pedir permissão.

curl — POST /chats
CLIPS=$(jq -s '[.[].result.assets[0].assetId]' clip1.json clip2.json clip3.json)
chat join.json "$(jq -n --arg email "$EMAIL" --argjson files "$CLIPS" '{email: $email, files: $files,
  message: "Concatenate video_1, video_2, video_3 in order into one MP4 with ffmpeg, keep the audio, save it to workspace/your_files/short.mp4 and attach it here. No public links."}')"
SHORT=$(jq -r '[.messages[] | select(.role == "assistant") | .files[] | select(.type == "video")][-1].assetId' join.json)
echo "$SHORT"
# user:12345-email:6578616d706c6540676d61696c2e636f6d-video:ws~your_files~short.mp4

20 segundos depois, o Muse disse “Concatenated all three in order — 30 seconds, 720x1280, with audio kept.” e anexou o arquivo em uma mensagem própria, um video nos seus files. O asset id leva o nome do caminho no workspace, então dê a cada Short o seu próprio nome de arquivo. O ffmpeg copiou os streams sem recodificar: o Short tem exatamente 30,0 segundos, e as três falas estão nele, em ordem.

5. Baixar o Short

GET /assets/assetId devolve o MP4:

curl — GET /assets/assetId
download "$SHORT" tambora-short.mp4

Os clipes e o Short também ficam no workspace do Muse da conta, onde o app muse.ai os mostra, e o asset id do Short pode entrar no próximo job como video_1.

Uma voz fixa: texto para fala como audio_1

Uma descrição em comum dá o mesmo tipo de voz, não uma voz travada. Nos três clipes do Tambora, o tom mediano do narrador ficou entre 105 e 117 Hz, menos de dois semitons de diferença, mas o Muse dá voz a cada clipe do zero. Para ter uma voz só, com certeza, faça o Muse dizer antes cada fala com o texto para fala dele, depois passe esse MP3 ao job de vídeo como audio_1. O chat de texto para fala levou 20 segundos no nosso teste.

curl — POST /chats e POST /jobs/video
chat voice1.json "$(jq -n --arg email "$EMAIL" --arg line "$(jq -r '.scenes[0].line' scenes.json)" '{email: $email,
  message: "Generate one mp3 file with text to speech: single speaker, voice Aria (avocado_v2:MAI_01), speed 100, language en, clean narration only: no music, no sound effects. Read exactly this text, do not change any word:\n\"\($line)\"\nSave it directly in workspace/your_files/ (not in a subfolder) as line-1.mp3 and attach it here. No public links and no uploads to remote storage."}')"
VOICE1=$(jq -r '[.messages[] | select(.role == "assistant") | .files[] | select(.type == "audio")][-1].assetId' voice1.json)

run clip1.json jobs/video "$(jq --arg email "$EMAIL" --arg voice "$VOICE1" '.scenes[0] | {email: $email, audio_1: $voice,
  prompt: "Vertical 9:16 video, about 10 seconds, no people on screen and nobody speaking on camera. \(.visual) Slow cinematic camera move, photoreal. Use audio_1 as the narration soundtrack exactly as it is: the off-screen narrator'\''s voice in audio_1, word for word, with no other voice and no new speech. Quiet ambient sound under it is fine."}' scenes.json)"
jq -r '.result.assets[] | select(.origin == "made") | .assetId' clip1.json

O Muse gera o clipe dele e depois coloca o audio_1 por cima com ffmpeg. O job lista os dois arquivos: a cópia final primeiro, com origin: "made", e o clipe bruto do Muse depois, com origin: "generated". Pegue o made. O clipe bruto traz uma voz que o Muse inventou, não a sua. O job de vídeo levou 108 segundos no nosso teste. Repita para cada cena e junte os clipes made como no passo 4.

Perguntas frequentes

Existe API do Meta Muse (muse.ai)? Com a useapi.net, sim: a Muse API é uma API de terceiros que roda o Muse na sua assinatura do site Muse. Gere imagens com POST /jobs/image e vídeos com POST /jobs/video. A Model API oficial da Meta vende o modelo Muse Image a $0,01 por imagem, mas, em outubro de 2026, não tem geração de vídeo nem API para o agente Muse em muse.ai, onde os vídeos são feitos. A nossa comparação de APIs de vídeo do Meta Muse (em inglês) cobre também o muse2api.

Funciona no plano gratuito do Muse? Com a useapi.net, sim: este passo a passo foi feito no plano gratuito (HATCH_FREE), que tem uma cota semanal. Um vídeo gastou de 2 a 3 por cento dela e uma imagem de 0 a 1 por cento, então uma conta gratuita gera mais ou menos 30 a 50 vídeos por semana. Os planos pagos Power ($20 por mês) e Maximum ($100 por mês) aumentam a cota (em outubro de 2026).

O Muse faz vídeo com mais de 10 segundos? Com a useapi.net, não em um único job: todo vídeo tem cerca de 10 segundos. Gere um clipe por cena e peça a um chat que junte os clipes com ffmpeg, como em Faça um Short narrado de 30 segundos, em que três clipes viraram um Short em cerca de 2 a 3 minutos. Um chat aceita até 3 vídeos.

Como definir a proporção, a duração ou a resolução? Com a useapi.net, no prompt, porque o Muse não tem parâmetros para isso. A proporção é respeitada quando escrita como in exactly 16:9 aspect ratio, e um clipe de imagem para vídeo mantém o formato da imagem, a menos que o prompt peça outro. Duração e resolução são fixas: todo vídeo volta em cerca de 720p com som, peça o prompt o que pedir.

Posso usar as minhas próprias fotos e vídeos? Com a useapi.net, sim: envie com POST /assets, até 50 MB cada, e passe o assetId como image_1..image_9, do mesmo jeito que você passa o resultado de um job anterior. Jobs de vídeo também aceitam video_1..video_3 e audio_1..audio_3. Todas as entradas de um job precisam estar na mesma conta.

Como fazer uma foto falar? Com a useapi.net, passe a foto como image_1 para POST /jobs/video com um prompt como Animate image_1: … she says softly: "…". O Muse mantém o formato da foto e diz a fala entre aspas, como no passo 7, em que um retrato restaurado de cem anos levanta os olhos e fala.

Dá para trocar uma pessoa em um vídeo? Com a useapi.net, sim: passe o clipe como video_1 e uma foto da nova pessoa como image_1, e peça ao Muse que troque a pessoa e mantenha todo o resto, como no passo 8. Diga o que precisa continuar igual, como o passeio, a ponte e as ondas, e dê à nova pessoa uma fala entre aspas se ela deve falar.

Por que a minha requisição síncrona devolveu 202? Com a useapi.net, um 202 quer dizer que o job ainda estava rodando depois de cerca de 100 segundos, e ele continua rodando do nosso lado. Busque o job com GET /jobs/jobid, mande jobs longos com async: true e consulte, ou acrescente um replyUrl para receber o registro do job pronto. Nos nossos testes, um vídeo levou de 68 a 138 segundos.

Por que o Muse me mandou um código de login por e-mail? Com a useapi.net, a API entrou na sua conta Muse, o que acontece quando você a adiciona e quando a API renova a sessão de 30 dias, antes de ela expirar ou sempre que o Muse pede uma nova entrada. O Muse manda um código de login por e-mail a cada entrada, e não é preciso fazer nada com ele. Veja o Setup Muse.

Qual a diferença para o muse2api? Com a useapi.net, a Muse API é hospedada, então não há nada para implantar: você adiciona cada conta com o e-mail e a senha e nós a mantemos conectada. Os resultados se encadeiam por asset id, e um job pronto pode chamar o seu webhook. Você também pode conversar com o agente Muse, com as respostas transmitidas ao vivo. O muse2api é um projeto que você mesmo implanta, hospeda e mantém rodando.

Conclusão

Visite o nosso servidor no Discord ou o canal no Telegram para qualquer dúvida de suporte.

Veja o nosso repositório no GitHub com exemplos de código.

As mesmas chamadas estão disponíveis como ferramentas no servidor MCP do Muse hospedado, para Claude Code, Cursor e VS Code.

Publicado também em

Também publicado no dev.to, discutido no Reddit, compartilhado no Bluesky e no YouTube como uma demo de 2:49 da API inteira (todos em inglês).

Comece a usar a API

Uma assinatura de US$ 15 por mês dá acesso a todas as APIs da useapi.net: um token, todos os serviços. Você conecta até 3 contas suas por serviço, e a useapi.net cuida da API REST, do balanceamento de carga e do polling. Precisa de mais contas? Cada assinatura extra custa US$ 10 por mês e acrescenta mais 3 contas por serviço.

Assinar — US$ 15/mês

Não tem cartão de crédito internacional? Você também pode assinar com criptomoedas (pagamentos em cripto não são reembolsáveis). Preços e planos de volume estão na página de assinatura. Pagando pelo Stripe, você recebe reembolso integral em até 14 dias após a primeira compra, se tiver menos de 50 gerações bem-sucedidas. Já é assinante? Altere a quantidade no portal do cliente Stripe em vez de comprar de novo.