npm.io
0.6.5 • Published yesterday

@apicity/fal

Licence
MIT
Version
0.6.5
Deps
1
Size
401 kB
Vulns
0
Weekly
0

@apicity/fal

npm dependencies TypeScript

Fal Platform API provider for model management, pricing, usage, and analytics.

Runtime dependencies:

  • zod@^4.4.3 — request schemas attached to every POST endpoint as .schema

Installation

npm install @apicity/fal
# or
pnpm add @apicity/fal

Quick Start

import { createFal } from "@apicity/fal";

const fal = createFal({ apiKey: process.env.FAL_API_KEY! });

Real-world example: upload a portrait, render a Sora 2 video

fal's signature flow is upload-once, reuse-everywhere — drop bytes onto fal's CDN via a presigned PUT, then thread the resulting https://*.fal.media/ URL through any model endpoint. The two-step snippet below combines tests/integration/fal-storage-upload-initiate.test.ts (POST initiate → PUT bytes) with tests/integration/fal-sora-2-image-to-video.test.ts (image-to-video generation), so every URL, byte count, and asset id below comes from real recorded HARs.

import { readFile } from "node:fs/promises";
import { createFal } from "@apicity/fal";

const fal = createFal({ apiKey: process.env.FAL_API_KEY! });

// 1. Reserve a signed upload slot. `initiate` returns two URLs: a
//    permanent `file_url` you'll feed to downstream models, and a
//    presigned `upload_url` you PUT the bytes to. Both point at the
//    same fal CDN — no third-party hosting needed.
const slot = await fal.storage.upload.initiate({
  file_name: "man.jpg",
  content_type: "image/jpeg",
});
console.log(slot.file_url);
// → "https://v3b.fal.media/files/b/0a96d564/QR9a1l-E0UuoR6zOHUMlX_man.jpg"
//   (the `cat1.jpg` recording shows the same URL shape with a
//    cat1 suffix; the suffix tracks `file_name` you passed in.)

// 2. PUT the bytes to the presigned URL. fal storage is plain HTTP —
//    no SDK call needed, just `fetch` with a matching Content-Type.
//    The signature on `upload_url` expires after a short window;
//    upload promptly. The resulting `file_url` is durable and
//    fetchable by every fal model endpoint.
const bytes = await readFile("./man.jpg");
const put = await fetch(slot.upload_url, {
  method: "PUT",
  headers: { "Content-Type": "image/jpeg" },
  body: bytes,
});
if (!put.ok) throw new Error(`upload failed: ${put.status}`);

// 3. Hand the now-permanent `file_url` to OpenAI's Sora 2 image-to-
//    video model. fal returns a typed bundle: the MP4, a webp
//    thumbnail, and a horizontal spritesheet — all hosted on the
//    same fal CDN. `duration` accepts 4 | 8 | 12 | 16 | 20 (seconds);
//    `aspect_ratio` is "auto" | "9:16" | "16:9".
const result = await fal.sora2.imageToVideo({
  prompt: "the man waves at the camera as the wind blows his hair",
  image_url: slot.file_url,
  aspect_ratio: "16:9",
  duration: 4,
});

console.log(result.video.url);
// → "https://v3b.fal.media/files/b/0a96bf3c/8U5wwkg9EC_eK0Jr3XyiR_Vgq1ZZPm.mp4"
console.log(result.video.file_size);
// → 2009236   // ~2 MB MP4 for a 4-second 720p clip
console.log(result.video_id);
// → "video_69e37804033c8191959194ea8aa8fc6e08bf9f3eb453b1b1"
console.log(result.thumbnail?.url);
// → "https://v3b.fal.media/files/b/0a96bf3c/bsgsaBd5IqdwOuufu_qSx_2yOP4u34.webp"
console.log(result.spritesheet?.url);
// → "https://v3b.fal.media/files/b/0a96bf3c/_9tqG1dEuRCEeegOulGrk_pWsHbiNB.bin"

Notes

  • The recorded sora-2 HAR inlines the image as a data:image/jpeg;base64,… URL — fal accepts both inline data URLs and any https:// URL it can reach. Uploading via fal storage first keeps request bodies tiny (350 KB → <1 KB) and lets you reuse the asset across multiple model calls without re-encoding.
  • The package re-exports a one-call uploadFile(provider, { data, filename, contentType }) helper that wraps the initiate-then-PUT dance and returns the file_url directly — use it when you don't need granular control over the lifecycle or signed URL.
  • Every POST endpoint exposes a Zod schema: call fal.sora2.imageToVideo.schema.safeParse(input) to validate a payload before paying for inference.
  • WAN 2.7 reference-to-video validates generated duration as 2–10 seconds. duration: 0 remains limited to source-clip edit-video flows where it means keeping the original clip length.
  • Long-running calls accept an AbortSignal second argument and compose with the package's middleware, e.g. withRetry(fal.sora2.imageToVideo, { retries: 3 }) from @apicity/fal to ride out transient queue / 429s.
  • Errors throw FalError with status, type, request_id, and the parsed body attached: try { ... } catch (e) { if (e instanceof FalError) console.error(e.status, e.body); }.

API Reference

70 endpoints across 18 groups. Each method mirrors an upstream URL path.

bytedance
POST fal.bytedance.seedSpeech.tts.v2

POST https://api.fal.ai/v1/fal-ai/bytedance/seed-speech/tts/v2

Upstream docs

const res = await fal.bytedance.seedSpeech.tts.v2({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

POST fal.bytedance.seedance2p0.fast.imageToVideo

POST https://api.fal.ai/v1/bytedance/seedance-2.0/fast/image-to-video

Upstream docs

const res = await fal.bytedance.seedance2p0.fast.imageToVideo({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

POST fal.bytedance.seedance2p0.fast.referenceToVideo

POST https://api.fal.ai/v1/bytedance/seedance-2.0/fast/reference-to-video

const res = await fal.bytedance.seedance2p0.fast.referenceToVideo({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

POST fal.bytedance.seedance2p0.fast.textToVideo

POST https://api.fal.ai/v1/bytedance/seedance-2.0/fast/text-to-video

Upstream docs

const res = await fal.bytedance.seedance2p0.fast.textToVideo({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

POST fal.bytedance.seedance2p0.imageToVideo

POST https://api.fal.ai/v1/bytedance/seedance-2.0/image-to-video

Upstream docs

const res = await fal.bytedance.seedance2p0.imageToVideo({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

POST fal.bytedance.seedance2p0.referenceToVideo

POST https://api.fal.ai/v1/bytedance/seedance-2.0/reference-to-video

const res = await fal.bytedance.seedance2p0.referenceToVideo({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

POST fal.bytedance.seedance2p0.textToVideo

POST https://api.fal.ai/v1/bytedance/seedance-2.0/text-to-video

Upstream docs

const res = await fal.bytedance.seedance2p0.textToVideo({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

POST fal.bytedance.seedream.v5.lite.edit

POST https://api.fal.ai/v1/fal-ai/bytedance/seedream/v5/lite/edit

Upstream docs

const res = await fal.bytedance.seedream.v5.lite.edit({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

POST fal.bytedance.seedream.v5.lite.textToImage

POST https://api.fal.ai/v1/fal-ai/bytedance/seedream/v5/lite/text-to-image

Upstream docs

const res = await fal.bytedance.seedream.v5.lite.textToImage({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

falAi
POST fal.falAi.elevenlabs.speechToText.scribeV2

POST https://api.fal.ai/v1/fal-ai/elevenlabs/speech-to-text/scribe-v2

Upstream docs

const res = await fal.falAi.elevenlabs.speechToText.scribeV2({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

gptImage1p5
POST fal.gptImage1p5

POST https://api.fal.ai/v1/fal-ai/gpt-image-1.5

Upstream docs

const res = await fal.gptImage1p5({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

POST fal.gptImage1p5.edit

POST https://api.fal.ai/v1/fal-ai/gpt-image-1.5/edit

Upstream docs

const res = await fal.gptImage1p5.edit({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

hunyuan
POST fal.hunyuan.v3.instructEdit

POST https://api.fal.ai/v1/fal-ai/hunyuan-image/v3/instruct/edit

Upstream docs

const res = await fal.hunyuan.v3.instructEdit({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

klingVideo
POST fal.klingVideo.o3p4k.imageToVideo

POST https://api.fal.ai/v1/fal-ai/kling-video/o3/4k/image-to-video

Upstream docs

const res = await fal.klingVideo.o3p4k.imageToVideo({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

POST fal.klingVideo.o3p4k.referenceToVideo

POST https://api.fal.ai/v1/fal-ai/kling-video/o3/4k/reference-to-video

Upstream docs

const res = await fal.klingVideo.o3p4k.referenceToVideo({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

POST fal.klingVideo.o3p4k.textToVideo

POST https://api.fal.ai/v1/fal-ai/kling-video/o3/4k/text-to-video

Upstream docs

const res = await fal.klingVideo.o3p4k.textToVideo({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

POST fal.klingVideo.v3.pro.imageToVideo

POST https://api.fal.ai/v1/fal-ai/kling-video/v3/pro/image-to-video

Upstream docs

const res = await fal.klingVideo.v3.pro.imageToVideo({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

POST fal.klingVideo.v3.pro.textToVideo

POST https://api.fal.ai/v1/fal-ai/kling-video/v3/pro/text-to-video

Upstream docs

const res = await fal.klingVideo.v3.pro.textToVideo({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

POST fal.klingVideo.v3.standard.imageToVideo

POST https://api.fal.ai/v1/fal-ai/kling-video/v3/standard/image-to-video

Upstream docs

const res = await fal.klingVideo.v3.standard.imageToVideo({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

POST fal.klingVideo.v3.standard.textToVideo

POST https://api.fal.ai/v1/fal-ai/kling-video/v3/standard/text-to-video

Upstream docs

const res = await fal.klingVideo.v3.standard.textToVideo({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

models
DELETE fal.v1.models.requests.payloads

DELETE https://api.fal.ai/v1/models/requests/{param}/payloads

Upstream docs

const res = await fal.v1.models.requests.payloads({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

GET fal.v1.models

GET https://api.fal.ai/v1/models

Upstream docs

const res = await fal.v1.models({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

GET fal.v1.models.pricing

GET https://api.fal.ai/v1/models/pricing

Upstream docs

const res = await fal.v1.models.pricing({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

GET fal.v1.models.requests.byEndpoint

GET https://api.fal.ai/v1/models/requests/by-endpoint

Upstream docs

const res = await fal.v1.models.requests.byEndpoint({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

GET fal.v1.models.requests.payloads

GET https://api.fal.ai/v1/models/requests/{param}/payloads

Upstream docs

const res = await fal.v1.models.requests.payloads({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

POST fal.v1.models.pricing.estimate

POST https://api.fal.ai/v1/models/pricing/estimate

Upstream docs

const res = await fal.v1.models.pricing.estimate({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

GET fal.v1.models

GET https://api.fal.ai/v1/models

Upstream docs

const res = await fal.v1.models({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

GET fal.v1.models.pricing

GET https://api.fal.ai/v1/models/pricing

Upstream docs

const res = await fal.v1.models.pricing({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

POST fal.v1.models.pricing.estimate

POST https://api.fal.ai/v1/models/pricing/estimate

Upstream docs

const res = await fal.v1.models.pricing.estimate({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

GET fal.v1.models.requests.byEndpoint

GET https://api.fal.ai/v1/models/requests/by-endpoint

Upstream docs

const res = await fal.v1.models.requests.byEndpoint({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

DELETE fal.v1.models.requests.payloads

DELETE https://api.fal.ai/v1/models/requests/{param}/payloads

Upstream docs

const res = await fal.v1.models.requests.payloads({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

nanoBanana
POST fal.nanoBanana.edit

POST https://api.fal.ai/v1/fal-ai/nano-banana/edit

Upstream docs

const res = await fal.nanoBanana.edit({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

POST fal.nanoBanana.textToImage

POST https://api.fal.ai/v1/fal-ai/nano-banana

Upstream docs

const res = await fal.nanoBanana.textToImage({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

nanoBanana2
POST fal.nanoBanana2.edit

POST https://api.fal.ai/v1/fal-ai/nano-banana-2/edit

Upstream docs

const res = await fal.nanoBanana2.edit({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

POST fal.nanoBanana2.textToImage

POST https://api.fal.ai/v1/fal-ai/nano-banana-2

Upstream docs

const res = await fal.nanoBanana2.textToImage({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

nanoBananaPro
POST fal.nanoBananaPro.edit

POST https://api.fal.ai/v1/fal-ai/nano-banana-pro/edit

Upstream docs

const res = await fal.nanoBananaPro.edit({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

POST fal.nanoBananaPro.textToImage

POST https://api.fal.ai/v1/fal-ai/nano-banana-pro

Upstream docs

const res = await fal.nanoBananaPro.textToImage({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

queue
POST fal.v1.queue.submit

POST https://api.fal.ai/v1/POST

Upstream docs

const res = await fal.v1.queue.submit({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

POST fal.v1.queue.submit

POST https://api.fal.ai/v1/POST

Upstream docs

const res = await fal.v1.queue.submit({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

qwenImage
POST fal.qwenImage

POST https://api.fal.ai/v1/fal-ai/qwen-image

Upstream docs

const res = await fal.qwenImage({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

POST fal.qwenImage.edit

POST https://api.fal.ai/v1/fal-ai/qwen-image-edit

Upstream docs

const res = await fal.qwenImage.edit({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

serverless
POST fal.v1.serverless.logs

POST https://api.fal.ai/v1/serverless/logs/stream

Upstream docs

const res = await fal.v1.serverless.logs({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

POST fal.v1.serverless.files.uploadLocal

POST https://api.fal.ai/v1/serverless/files/file/local/{param}

Upstream docs

const res = await fal.v1.serverless.files.uploadLocal({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

POST fal.v1.serverless.files.uploadUrl

POST https://api.fal.ai/v1/serverless/files/file/url/{param}

Upstream docs

const res = await fal.v1.serverless.files.uploadUrl({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

POST fal.v1.serverless.files.uploadLocal

POST https://api.fal.ai/v1/serverless/files/file/local/{param}

Upstream docs

const res = await fal.v1.serverless.files.uploadLocal({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

POST fal.v1.serverless.files.uploadUrl

POST https://api.fal.ai/v1/serverless/files/file/url/{param}

Upstream docs

const res = await fal.v1.serverless.files.uploadUrl({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

POST fal.v1.serverless.logs

POST https://api.fal.ai/v1/serverless/logs/stream

Upstream docs

const res = await fal.v1.serverless.logs({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

sora2
POST fal.sora2.imageToVideo

POST https://api.fal.ai/v1/fal-ai/sora-2/image-to-video

Upstream docs

const res = await fal.sora2.imageToVideo({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

POST fal.sora2.textToVideo

POST https://api.fal.ai/v1/fal-ai/sora-2/text-to-video

Upstream docs

const res = await fal.sora2.textToVideo({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

storage
POST fal.storage.upload.completeMultipart

POST https://rest.fal.ai/storage/upload/complete-multipart

Upstream docs

const res = await fal.storage.upload.completeMultipart({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

POST fal.storage.upload.initiate

POST https://rest.fal.ai/storage/upload/initiate

Upstream docs

const res = await fal.storage.upload.initiate({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

POST fal.storage.upload.initiateMultipart

POST https://rest.fal.ai/storage/upload/initiate-multipart

Upstream docs

const res = await fal.storage.upload.initiateMultipart({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

veo3p1
POST fal.veo3p1.imageToVideo

POST https://api.fal.ai/v1/fal-ai/veo3.1/image-to-video

Upstream docs

const res = await fal.veo3p1.imageToVideo({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

POST fal.veo3p1.textToVideo

POST https://api.fal.ai/v1/fal-ai/veo3.1

Upstream docs

const res = await fal.veo3p1.textToVideo({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

wan
POST fal.wan.v2p7.edit

POST https://api.fal.ai/v1/fal-ai/wan/v2.7/edit

Upstream docs

const res = await fal.wan.v2p7.edit({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

POST fal.wan.v2p7.editVideo

POST https://api.fal.ai/v1/fal-ai/wan/v2.7/edit-video

const res = await fal.wan.v2p7.editVideo({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

POST fal.wan.v2p7.imageToVideo

POST https://api.fal.ai/v1/fal-ai/wan/v2.7/image-to-video

Upstream docs

const res = await fal.wan.v2p7.imageToVideo({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

POST fal.wan.v2p7.pro.edit

POST https://api.fal.ai/v1/fal-ai/wan/v2.7/pro/edit

Upstream docs

const res = await fal.wan.v2p7.pro.edit({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

POST fal.wan.v2p7.pro.textToImage

POST https://api.fal.ai/v1/fal-ai/wan/v2.7/pro/text-to-image

Upstream docs

const res = await fal.wan.v2p7.pro.textToImage({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

POST fal.wan.v2p7.referenceToVideo

POST https://api.fal.ai/v1/fal-ai/wan/v2.7/reference-to-video

const res = await fal.wan.v2p7.referenceToVideo({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

POST fal.wan.v2p7.textToImage

POST https://api.fal.ai/v1/fal-ai/wan/v2.7/text-to-image

Upstream docs

const res = await fal.wan.v2p7.textToImage({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

POST fal.wan.v2p7.textToVideo

POST https://api.fal.ai/v1/fal-ai/wan/v2.7/text-to-video

Upstream docs

const res = await fal.wan.v2p7.textToVideo({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

workflows
GET fal.v1.workflows

GET https://api.fal.ai/v1/workflows

Upstream docs

const res = await fal.v1.workflows({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

GET fal.v1.workflows

GET https://api.fal.ai/v1/workflows

Upstream docs

const res = await fal.v1.workflows({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

xai
POST fal.xai.grokImagineImage

POST https://api.fal.ai/v1/xai/grok-imagine-image

Upstream docs

const res = await fal.xai.grokImagineImage({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

POST fal.xai.grokImagineImage.edit

POST https://api.fal.ai/v1/xai/grok-imagine-image/edit

Upstream docs

const res = await fal.xai.grokImagineImage.edit({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

POST fal.xai.grokImagineVideo.editVideo

POST https://api.fal.ai/v1/xai/grok-imagine-video/edit-video

Upstream docs

const res = await fal.xai.grokImagineVideo.editVideo({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

POST fal.xai.grokImagineVideo.extendVideo

POST https://api.fal.ai/v1/xai/grok-imagine-video/extend-video

Upstream docs

const res = await fal.xai.grokImagineVideo.extendVideo({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

POST fal.xai.grokImagineVideo.imageToVideo

POST https://api.fal.ai/v1/xai/grok-imagine-video/image-to-video

Upstream docs

const res = await fal.xai.grokImagineVideo.imageToVideo({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

POST fal.xai.grokImagineVideo.referenceToVideo

POST https://api.fal.ai/v1/xai/grok-imagine-video/reference-to-video

Upstream docs

const res = await fal.xai.grokImagineVideo.referenceToVideo({ /* ... */ });

Source: packages/provider/fal/src/fal.ts

Middleware

import { createFal, withRetry } from "@apicity/fal";

const fal = createFal({ apiKey: process.env.FAL_API_KEY! });
const models = withRetry(fal.get.v1.models, { retries: 3 });

Part of the apicity monorepo.

License

MIT — see LICENSE.

Keywords