SDK @fal-ai/client v1.x
Next.js 14 App Router
FAL_KEY requerida
Webhooks prod-ready
TypeScript + Python
Modelos disponibles para VisualGen
Flux Schnell
Generación más rápida. 4 pasos, ~2s. Ideal para borradores y previews en tiempo real.
~2s · Borrador
Flux Pro
Máxima calidad fotorrealista. 28 pasos, guidance 3.5. Producción de assets finales.
Alta Calidad
Flux Dev + LoRA
Fine-tuning por marca. Carga LoRAs de HuggingFace para estilos personalizados.
Brand Style
AnimateDiff
Anima creatividades estáticas. 16 frames, 8fps. MP4 en segundos.
Vídeo
Whisper v3
Transcripción de podcasts y locuciones. Auto-detección de idioma, salida por segmentos.
Audio → Texto
MusicGen
Música de fondo a medida. Prompt en texto, duración configurable, 120 BPM +.
Música
Comparativa de modelos Flux
| Modelo ID | Calidad | Velocidad | Pasos | Uso recomendado |
|---|---|---|---|---|
| fal-ai/flux/schnell | Buena | ~2s | 4 | Previews, borradores, batch alto volumen |
| fal-ai/flux/dev | Alta | ~8s | 28 | Assets intermedios, iteración rápida |
| fal-ai/flux-pro | Máxima | ~15s | 28 | Assets finales, publicaciones cliente |
| fal-ai/flux-lora | Alta + Brand | ~12s | 28 | Identidad de marca, estilos personalizados |
| fal-ai/flux/dev/image-to-image | Alta | ~10s | 28 | Transformar imágenes existentes del cliente |
Instalación y configuración
bash Instalación
# Instalar SDK JavaScript/TypeScript npm install @fal-ai/client # O para Python pip install fal-client
bash Variables de entorno
# .env.local (Next.js) FAL_KEY="fal_sk_xxxxxxxxxxxxxxxxxxxxxxxx" # Verificar en fal.ai/dashboard → API Keys # NUNCA exponer al cliente (usa route handlers)
Generación de imágenes — Flux
ts app/api/generate-image/route.ts — VisualGen
Producción
import * as fal from "@fal-ai/client"; import { NextRequest, NextResponse } from "next/server"; fal.config({ credentials: process.env.FAL_KEY }); export async function POST(req: NextRequest) { const { prompt, quality, size } = await req.json(); // Selección dinámica de modelo según calidad solicitada const modelId = quality === "draft" ? "fal-ai/flux/schnell" // Borrador: 4 pasos, ~2s : "fal-ai/flux-pro"; // Final: 28 pasos, alta calidad const result = await fal.subscribe(modelId, { input: { prompt: prompt, image_size: size ?? "landscape_4_3", num_images: 1, num_inference_steps: quality === "draft" ? 4 : 28, guidance_scale: 3.5, safety_tolerance: "2", }, onQueueUpdate: (update) => { if (update.status === "IN_PROGRESS") { console.log("[fal] progreso:", update.logs?.at(-1)?.message); } }, }); return NextResponse.json({ url: result.data.images[0].url, seed: result.data.seed, model: modelId, }); }
Pipeline asíncrono con webhooks
ts Pipeline async: submit → webhook → Supabase
Pipeline Producción
// 1. SUBMIT — app/api/jobs/submit/route.ts import { supabase } from "@/lib/supabase"; export async function POST(req: NextRequest) { const { prompt, userId } = await req.json(); // Enviar job y retornar inmediatamente const { request_id } = await fal.queue.submit("fal-ai/flux-pro", { input: { prompt, num_images: 1 }, webhookUrl: `${process.env.NEXT_PUBLIC_URL}/api/webhooks/fal`, }); // Guardar en Supabase con estado pendiente await supabase.from("generations").insert({ request_id, userId, prompt, status: "pending" }); return NextResponse.json({ request_id, status: "queued" }); } // 2. WEBHOOK — app/api/webhooks/fal/route.ts export async function POST(req: Request) { const payload = await req.json(); if (payload.status === "OK") { const imageUrl = payload.payload.images[0].url; await supabase.from("generations").update({ image_url: imageUrl, status: "completed" }).eq("request_id", payload.request_id); // Notificar al usuario vía Supabase Realtime } return Response.json({ ok: true }); }
Audio: transcripción Whisper y música MusicGen
ts Whisper — Transcripción
import * as fal from "@fal-ai/client"; import * as fs from "fs"; // 1. Subir archivo de audio const audioBuffer = fs.readFileSync("podcast.mp3"); const audioUrl = await fal.storage.upload( audioBuffer, "podcast.mp3" ); // 2. Transcribir con Whisper v3 const result = await fal.subscribe("fal-ai/whisper", { input: { audio_url: audioUrl, task: "transcribe", language: "es", chunk_level: "segment", version: "3", } }); // Resultado: texto + timestamps por segmento console.log(result.data.text);
py MusicGen — Música de fondo
import fal_client # Generar música corporativa para vídeo result = fal_client.subscribe( "fal-ai/musicgen", arguments={ "prompt": ( "Upbeat corporate background music, " "professional, 120 BPM, no vocals, " "suitable for product demo video" ), "duration": 30, # segundos "top_k": 250, "temperature": 1.0, } ) # URL del archivo .wav generado print(result["audio"]["url"])
Procesamiento en lote — Campañas VisualGen
ts Generación paralela de múltiples creatividades
Alto Volumen
// Generar todas las variantes de una campaña en paralelo async function generateCampaignAssets(prompts: string[]): Promise<string[]> { // Enviar todos los jobs simultáneamente (sin esperar a que terminen) const jobs = await Promise.all( prompts.map(prompt => fal.queue.submit("fal-ai/flux/schnell", { input: { prompt, num_images: 1, num_inference_steps: 4 } }) ) ); // Recoger todos los resultados (se procesan en paralelo en fal.ai) const results = await Promise.all( jobs.map(({ request_id }) => fal.queue.result("fal-ai/flux/schnell", { requestId: request_id }) ) ); return results.map(r => r.data.images[0].url); } // Uso: generar 6 variantes de campaña de verano const urls = await generateCampaignAssets([ "Summer sale banner, beach lifestyle, vibrant colors, 16:9", "Summer sale banner, outdoor sports, energetic, 16:9", "Summer sale product shot, minimal white background, 1:1", "Summer sale Instagram story, vertical, gradient sunset", "Summer sale email header, tropical vibes, 600px wide", "Summer sale Facebook ad, family fun, warm tones", ]); // ~2s total para 6 imágenes en paralelo
Arquitectura de producción — VisualGen
Flujo async: Frontend → API → fal.ai → Webhook → Supabase Realtime
Next.js
Frontend
+ App Router
+ App Router
→
Route Handler
queue.submit()
server-side
server-side
→
fal.ai GPU
Flux Pro / Whisper
AnimateDiff
AnimateDiff
→
Webhook
/api/webhooks/fal
resultado listo
resultado listo
→
Supabase
Storage + DB
Realtime notify
Realtime notify
Estimación de costes para VisualGen
Imagen
Flux Schnell
$0.003
por imagen
1.000 borradores/mes
= $3/mes
= $3/mes
Imagen
Flux Pro
$0.055
por imagen
500 imágenes finales/mes
= $27.5/mes
= $27.5/mes
Vídeo
AnimateDiff
$0.05
por 16 frames
200 animaciones/mes
= $10/mes
= $10/mes
Audio
Whisper v3
$0.008
por minuto audio
100 podcasts × 30min
= $24/mes
= $24/mes
Checklist de implementación
SDK instalado: npm install @fal-ai/client — sin peer-deps conflictivos con Next.js 14
FAL_KEY en servidor: Solo usar en Route Handlers / Server Actions. NUNCA en componentes cliente ni NEXT_PUBLIC_*
subscribe() vs queue.submit(): Subscribe para jobs cortos (<30s), queue+webhook para Flux Pro y vídeo
Subida de archivos: fal.storage.upload() antes de pasar URLs de audio o imágenes de referencia a los modelos
Persistir outputs: Las URLs de fal.run/files/ son temporales — descargar y subir a Supabase Storage inmediatamente
Batch en paralelo: Promise.all con queue.submit para campañas — fal.ai procesa todos simultáneamente sin overhead
onQueueUpdate: Implementar para mostrar progreso en la UI — los logs del modelo llegan en tiempo real