View raw .mdDownload

Fal SDK through AI Pass

Use the official @fal-ai/client with AI Pass as its authenticated queue proxy. You keep Fal's endpoint IDs, input objects, queue methods, and result shapes; AI Pass supplies the Fal account, charges the caller's AI Pass wallet, and settles the request from Fal's billing evidence.

You do not need a Fal account or a Fal API key.

This integration covers Fal's queue workflow: subscribe, queue.submit, queue.status, queue.result, and queue.cancel. It does not proxy synchronous run, streaming, realtime, webhooks, or Fal storage uploads.

Choose the right authentication setup

Where the code runsAI Pass credentialFal client configuration
Node.js, Bun, a serverless function, or another trusted backendAI Pass API keySet credentials and force the proxy with when: "always"
Browser applicationAI Pass OAuth tokenInitialize the AI Pass Web SDK and pass AiPass.falConfig()

Never put an AI Pass API key in browser code. Browser apps should use AI Pass OAuth so every user authorizes the app and pays from their own wallet.

Server-side setup with an AI Pass API key

1. Install the official Fal client

npm install @fal-ai/client

AI Pass tests the proxy against @fal-ai/client 1.10.1.

2. Store your AI Pass key in the environment

export AIPASS_API_KEY="your-ai-pass-api-key"

Create or manage API keys in the AI Pass Developer Dashboard.

3. Create the Fal client

import { createFalClient } from "@fal-ai/client";

const apiKey = process.env.AIPASS_API_KEY;
if (!apiKey) {
  throw new Error("AIPASS_API_KEY is required");
}

export const fal = createFalClient({
  credentials: apiKey,
  proxyUrl: {
    url: "https://aipass.one/v1/fal/proxy",
    when: "always"
  }
});

The object form of proxyUrl matters on a backend. A plain string uses the proxy only in browser runtimes by default; when: "always" guarantees that Node.js and other server runtimes also send the request through AI Pass.

Do not set FAL_KEY. The credential in this configuration is an AI Pass API key, not a Fal key.

Browser setup with AI Pass OAuth

The browser flow uses both SDKs:

<script src="https://aipass.one/aipass-sdk.js"></script>

<script type="module">
  import { createFalClient } from "https://esm.sh/@fal-ai/[email protected]";

  AiPass.initialize({
    clientId: "your_ai_pass_oauth_client_id",
    requireLogin: true
  });

  const fal = createFalClient(AiPass.falConfig());

  const result = await fal.subscribe("fal-ai/flux/schnell", {
    input: {
      prompt: "A quiet Mediterranean street at golden hour",
      image_size: "landscape_16_9",
      num_images: 1
    }
  });

  console.log(result.data.images[0].url);
</script>

AiPass.falConfig():

  • points the Fal client at https://aipass.one/v1/fal/proxy;
  • obtains an AI Pass OAuth token when needed;
  • attaches the current token and OAuth client binding to every request; and
  • uses a refreshed token for later status, result, and cancel calls.

Do not pass credentials in the browser. Do not embed an API key in JavaScript, an environment variable exposed by a frontend build, or a public repository.

See the AI Pass Web SDK guide for OAuth client setup and login behavior.

Call a model with subscribe

subscribe is the simplest and recommended flow. It submits the job, polls its status, and returns the result after completion.

const endpoint = "fal-ai/flux/schnell";

const result = await fal.subscribe(endpoint, {
  input: {
    prompt: "A glass observatory above the clouds, editorial photography",
    image_size: "landscape_16_9",
    num_images: 1
  },
  mode: "polling",
  pollInterval: 1000,
  timeout: 180_000,
  onQueueUpdate(update) {
    console.log("Fal status:", update.status);
  }
});

console.log("Request:", result.requestId);
console.log("Image:", result.data.images[0].url);

Possible queue states are IN_QUEUE, IN_PROGRESS, and COMPLETED. The result payload under result.data is Fal's original model-specific output document. For example, an image model commonly returns data.images, while a video model may return data.video.

AI Pass does not manufacture Fal runtime logs, queue positions, or metrics. Use onQueueUpdate for lifecycle state, not as a guaranteed log stream.

Choose the model and parameters

Browse fal.ai/models, open a model, and copy its exact endpoint ID. AI Pass does not translate that ID.

The model's Fal page is also the source of truth for its input fields, accepted values, defaults, and output shape. Place those fields inside input exactly as Fal documents them:

await fal.subscribe("exact/fal-endpoint-id", {
  input: {
    // The model's documented Fal input object goes here.
  }
});

There is no universal AI Pass parameter list for Fal models.

How text-to-image and text-to-video are selected

The endpoint ID selects the operation. There is no separate type parameter.

  • A text-to-image endpoint receives a text-to-image input object.
  • A text-to-video endpoint receives a text-to-video input object.
  • An image-to-video endpoint normally requires an image_url in addition to its motion prompt.
  • Some endpoint IDs end in /text-to-image or /text-to-video; others, such as fal-ai/veo3.1, express the capability through the model itself.

Always copy the precise endpoint variant from Fal. Do not send type: "video" or try to turn an image endpoint into a video endpoint with a body field.

Text-to-image example with quality

const result = await fal.subscribe("fal-ai/gpt-image-1/text-to-image", {
  input: {
    prompt: "A premium product photograph of a silver mechanical watch",
    image_size: "1024x1024",
    quality: "high",
    num_images: 1,
    output_format: "png",
    background: "auto"
  }
});

console.log(result.data.images[0].url);

quality is a model input, not a Fal SDK option. Use it only when the chosen model's Fal input schema declares it. Other models may express quality through resolution, image_size, inference steps, or a different endpoint variant.

Text-to-video example with duration

const result = await fal.subscribe("fal-ai/veo3.1", {
  input: {
    prompt: "A slow aerial move over a volcanic island at sunrise",
    duration: "8s",
    resolution: "720p",
    aspect_ratio: "16:9",
    generate_audio: true
  },
  mode: "polling",
  pollInterval: 2000,
  timeout: 10 * 60_000,
  onQueueUpdate(update) {
    console.log(update.status);
  }
});

console.log(result.data.video.url);

duration is also a model input. Its type and allowed vocabulary vary by endpoint:

  • one model may accept a suffixed string such as "8s";
  • another may accept a string such as "8";
  • another may require the number 8; and
  • some models define length through num_frames and fps instead.

Do not normalize these values yourself. Send the exact form documented on the selected Fal model page.

Image-to-video inputs

Use a reachable HTTPS URL when the endpoint needs an image:

const result = await fal.subscribe("your/image-to-video-endpoint", {
  input: {
    prompt: "The camera moves slowly toward the subject",
    image_url: "https://cdn.example.com/input.jpg",
    // Add the endpoint's documented duration, resolution, and other fields.
  }
});

Do not pass a browser File or Blob. The Fal client handles those by calling Fal's storage API, and AI Pass does not proxy that API. Upload the file to a location Fal can fetch over HTTPS, then pass its URL. Generated Fal media URLs can expire, so save or download important results promptly.

Run the queue lifecycle manually

Use the queue methods when you need to return a request ID immediately, persist it, poll from another worker, or provide a cancel button.

Submit

const endpoint = "fal-ai/flux/schnell";

const queued = await fal.queue.submit(endpoint, {
  input: {
    prompt: "A small cabin beside a frozen lake",
    image_size: "landscape_16_9"
  }
});

const requestId = queued.request_id;
console.log(requestId, queued.status);

Check status

const status = await fal.queue.status(endpoint, {
  requestId,
  logs: false
});

console.log(status.status);

Retrieve the result

Call result only after the status becomes COMPLETED:

const result = await fal.queue.result(endpoint, { requestId });
console.log(result.data);

Use the same endpoint ID and the same AI Pass credential that created the request. A different API key or a token from another OAuth app cannot retrieve or cancel it.

Cancel

await fal.queue.cancel(endpoint, { requestId });

Cancellation is best effort. A model that has already completed or reached a non-cancellable execution stage may still finish and may still incur provider cost.

Complete polling helper

const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));

async function runQueued(fal, endpoint, input) {
  const queued = await fal.queue.submit(endpoint, { input });
  const requestId = queued.request_id;

  while (true) {
    const status = await fal.queue.status(endpoint, { requestId });

    if (status.status === "COMPLETED") {
      return fal.queue.result(endpoint, { requestId });
    }

    await delay(1500);
  }
}

If execution failed upstream, the status lifecycle finishes and the result call returns Fal's error rather than an empty success document.

Retry safety and idempotency

Fal normally creates a new stochastic generation for every submission. AI Pass preserves that behavior: two identical calls create two jobs.

If your own network retry must resolve to the first job instead of creating another one, supply an idempotency key:

const queued = await fal.queue.submit("fal-ai/flux/schnell", {
  input: {
    prompt: "A red paper boat on black water"
  },
  headers: {
    "Idempotency-Key": "image-order-1842-attempt-1"
  }
});

An AI Pass idempotency key must contain 8–128 letters, numbers, dots, underscores, colons, or hyphens. Reuse it only for a retry of the same logical request. Use a new key when the user deliberately asks for another generation.

Errors

The Fal client exposes ApiError and ValidationError. AI Pass returns Fal-compatible error documents, so existing Fal error handling continues to work.

import {
  ApiError,
  ValidationError,
  createFalClient
} from "@fal-ai/client";

try {
  const result = await fal.subscribe("fal-ai/veo3.1", {
    input: {
      prompt: "A lighthouse in a storm",
      duration: "8s"
    }
  });

  console.log(result.data);
} catch (error) {
  if (error instanceof ValidationError) {
    console.error("Invalid model input:", error.fieldErrors);
  } else if (error instanceof ApiError) {
    console.error("Request failed:", error.status, error.body);
  } else {
    console.error("Unexpected error:", error);
  }
}

Common statuses:

StatusMeaning
401The AI Pass key or OAuth token is missing, invalid, or expired
402The caller's AI Pass wallet cannot cover admission
404The endpoint or request is unavailable, or a provider result has expired
409The request is not in a state where that lifecycle action can run
413The JSON request body exceeds the bridge limit
422The model rejected an input field or value
429A rate limit was reached
502Fal or provider-cost evidence could not be reached or validated

For 422, read the returned field errors and compare the request with the model's current Fal input schema. Do not log API keys, OAuth tokens, prompts, private media URLs, or full provider results in production error logs.

Model availability, holds, and final charges

The models shown in AI Pass's Available Models catalog are curated routes. That catalog is not an allowlist for the Fal SDK bridge.

Any syntactically valid public Fal queue endpoint can be called through /v1/fal, including an endpoint that has no AI Pass catalog row. Private Fal endpoints still require whatever upstream access they define and are not made public by AI Pass.

Endpoint kindAdmission behavior
Curated by AI PassUses the known model schema and a model-specific maximum wallet hold
Not yet curatedUses an optimistic $0.01 admission hold, requires at least $0.01 available balance, and is limited to 20 submissions per caller per hour

The $0.01 optimistic hold is not the price or a price cap. Once Fal publishes the exact request-level billing event, AI Pass settles the real provider cost plus the applicable AI Pass and app pricing. The final charge can be higher or lower than the initial hold. Unused reserved funds are released.

If an uncurated endpoint becomes part of a high-volume production workflow, contact AI Pass so it can be curated with a model-specific input and pricing contract.

Supported Fal client surface

Fal client featureThrough AI PassNotes
fal.subscribe()YesUse polling mode
fal.queue.submit()YesReturns an AI Pass-backed Fal queue document
fal.queue.status()YesLifecycle status; runtime logs and metrics are not guaranteed
fal.queue.result()YesReturns Fal's original output document
fal.queue.cancel()YesBest-effort cancellation
fal.run()NoUses Fal's synchronous host rather than the queue bridge
fal.stream() / streaming statusNoUses protocols and paths outside the queue bridge
fal.realtimeNoUses WebSockets outside the queue bridge
fal.storage.upload()NoUpload files elsewhere and pass reachable HTTPS URLs
WebhooksNoPoll status or use subscribe

Migrate an existing Fal integration

If you already use fal.subscribe or the queue methods, the endpoint ID, input object, and result parsing can remain unchanged.

Before:

const fal = createFalClient({
  credentials: process.env.FAL_KEY
});

After:

const fal = createFalClient({
  credentials: process.env.AIPASS_API_KEY,
  proxyUrl: {
    url: "https://aipass.one/v1/fal/proxy",
    when: "always"
  }
});

You can then keep calls such as:

const result = await fal.subscribe("fal-ai/flux/schnell", {
  input: {
    prompt: "A geometric poster in cobalt and cream",
    image_size: "portrait_4_3"
  }
});

For a browser integration, remove the API key and your own Fal server proxy, initialize the AI Pass Web SDK, and use:

const fal = createFalClient(AiPass.falConfig());

Raw HTTP equivalent

The SDK proxy is recommended, but the same queue surface is available as ordinary HTTP. Replace Fal's queue origin with the AI Pass Fal base and keep the endpoint ID unchanged:

curl -X POST \
  "https://aipass.one/v1/fal/fal-ai/flux/schnell" \
  -H "Authorization: Key $AIPASS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "A quiet harbor at blue hour",
    "image_size": "landscape_16_9"
  }'

The response contains request_id, status_url, response_url, and cancel_url. Send the same credential when following those URLs.

Production checklist

  • Copy the exact endpoint ID and input schema from the model's current Fal page.
  • Keep API keys on trusted servers; use AiPass.falConfig() in browsers.
  • In Node.js or another non-browser runtime, set proxyUrl.when to "always".
  • Use the default polling queue workflow, not run, streaming, realtime, or webhooks.
  • Pass reachable HTTPS URLs instead of File or Blob inputs.
  • Treat duration, quality, resolution, and similar fields as model-specific inputs.
  • Persist the request ID when using manual queue methods.
  • Add an idempotency key only when retrying the same logical submission.
  • Handle 402, 422, and 429 explicitly.
  • Download important output media before its provider URL expires.

Want a fresh agent brief?

Choose your agent and copy one secure brief. It inspects the project, requests narrow setup approval, and creates only the public configuration it needs.

Run one-click setup

Stuck? We're happy to help on Discord

Active Discord community with the AI Pass team. Get unblocked on integration, ask about models, share what you're building.

Join AI Pass Discord