Provider Keys — Fal
Fal is the one provider on the key market that does not take its target in the URL. That single difference accounts for most first-call failures, so it is the first thing on this page.
Billing unit: per megapixel. Fal keeps no record of a call against the seller's account, so nothing you generate here persists on someone else's history.
The target is a header, and the path is empty
The target must be an absolute https URL on one of Fal's two model hosts. Anything else — Fal's storage,
pricing or account hosts, or any other domain — never leaves our fence.
| Host | What it is |
|---|---|
fal.run | Answers with the result itself. Synchronous. |
queue.fal.run | Starts a job and answers with an id. |
fal.runWhat it is · Answers with the result itself. Synchronous.queue.fal.runWhat it is · Starts a job and answers with an id.Using the SDK
@fal-ai/client is the normal way in. Point proxyUrl at our route and send the target always, not just
when the SDK feels like it:
import { fal } from '@fal-ai/client'; fal.config({ // No Fal key here — the seller's key is resolved on our side, at call time. proxyUrl: { url: '$SURPLUS_BASE_URL/resources/fal', when: 'always', }, requestMiddleware: async (request) => ({ ...request, headers: { ...request.headers, Authorization: `Bearer ${process.env.SURPLUS_API_KEY}` }, }), }); const result = await fal.subscribe('fal-ai/flux/schnell', { input: { prompt: 'a tiny red origami crane', image_size: 'square', num_images: 1 }, }); console.log(result.data.images[0].url);
# The Python client has no proxy option, so call the route directly. import os, requests r = requests.post( "$SURPLUS_BASE_URL/resources/fal", headers={ "Authorization": f"Bearer {os.environ['SURPLUS_API_KEY']}", "x-fal-target-url": "https://fal.run/fal-ai/flux/schnell", "Content-Type": "application/json", }, json={"prompt": "a tiny red origami crane", "image_size": "square", "num_images": 1}, timeout=60, ) print(r.headers["x-si-buyer-cost-micro"], r.headers["x-si-list-cost-micro"]) print(r.json()["images"][0]["url"])
when: 'always' matters. With the default the SDK proxies only some calls and sends the rest straight to Fal,
where your request has no credential and fails — with nothing on our side to explain why.
Fal is the one provider whose follow-up URLs we leave untouched: its SDK builds them itself and sends each target in the header, so it stays on your key without our help. Path-style providers get their follow-up URLs rewritten instead — see jobs and settlement.
What is reachable
Only Fal's own public models, on three methods:
| Rule | Covers |
|---|---|
POST /fal-ai/+ | Submit or run a model. |
GET /fal-ai/+ | Status and result. |
PUT /fal-ai/+ | Cancel. |
POST /fal-ai/+Covers · Submit or run a model.GET /fal-ai/+Covers · Status and result.PUT /fal-ai/+Covers · Cancel.fal-ai is the only publisher. That is not a preference — a seller's private apps live under
<their-username>/<app> and answer on these same two hosts to the seller's key, so no denylist could cover
them, while nobody else can deploy under fal-ai. An allowlist of one publisher is the only shape that is
safe by construction. Other public publishers are added as data once verified.
?logs=1 is the one query parameter passed through, because the SDK asks for run logs on status polls.
fal_webhook is deliberately not: it would send your result to a URL we do not control.
Two response headers come back untouched, because the SDK reads both: x-fal-request-id and
x-fal-request-timeout-type.
Two things Fal refuses here
LLMs
fal-ai/any-llm is Fal's unified LLM endpoint — Claude, GPT, Gemini, Llama — and it is denied provider-wide,
not per listing.
An LLM bills on output tokens, which no quote taken before the call can bound. That makes it a platform
pricing gap rather than a seller's choice, which is exactly why it is not left to the access editor. The rule is
unqualified across every method, not POST-only: a family refused on one verb and open on the others reads as
an oversight the next time someone edits it.
LLM resale is the order book's job, not this market's. Use /v1/chat/completions.
Models that can run for minutes
Fal prices most image models per megapixel or per image, and those answer inside the request. Everything else — video, 3D, long audio — can run for minutes.
- Priced per megapixel or imageRuns on fal.run, answers inline
- Priced any other wayRefused on fal.run — 400 job_route_required
- Submit to queue.fal.runSurvives a replaced task
The refusal names itself: job_route_required. Submit the same model to queue.fal.run instead and follow it
as a job. A queue submit answers immediately either way, so this costs you nothing but a different target host.
On the x402 rail there is no queue option — the message says so, and you need a Surplus API key for those models.
Timeouts
POST /fal-ai/+ is capped at 50 seconds, inside the fleet's 60-second drain, so a deploy never cuts a call
a seller is already billed for. A model that needs longer belongs on the queue.
A worked queue job
curl -X POST $SURPLUS_BASE_URL/resources/fal \ -H "Authorization: Bearer $SURPLUS_API_KEY" \ -H "x-fal-target-url: https://queue.fal.run/fal-ai/flux/dev" \ -H "Content-Type: application/json" \ -d '{"prompt":"a paper boat","image_size":"square"}' # → { "request_id": "764cabcf-…", "status_url": "…", "response_url": "…" }
# Fal's own URLs are left as they are; send them back through the header. curl $SURPLUS_BASE_URL/resources/fal \ -H "Authorization: Bearer $SURPLUS_API_KEY" \ -H "x-fal-target-url: https://queue.fal.run/fal-ai/flux/dev/requests/764cabcf-…/status?logs=1" # → { "status": "IN_QUEUE" | "IN_PROGRESS" | "COMPLETED" }
curl $SURPLUS_BASE_URL/resources/fal \ -H "Authorization: Bearer $SURPLUS_API_KEY" \ -H "x-fal-target-url: https://queue.fal.run/fal-ai/flux/dev/requests/764cabcf-…" # → the images, and this is where the charge settles
COMPLETED does not mean it worked. A failed Fal job also reads COMPLETED, with no error on the status, so
the outcome is only confirmed on the result: 2xx succeeded, 4xx failed. We settle on the same evidence you do.
Related
- Your first call — the end-to-end path, all three providers.
- Jobs and settlement — bindings, polling and when you are charged.
- Access rules — how a seller narrows a key further.
- Provider key endpoints — the endpoint reference.