@bria-ai/client is Bria's official TypeScript and JavaScript SDK. It wraps the v2 REST API with a single async client, handles the async job lifecycle (submit, poll, webhooks), retries transient failures, and converts local images to API inputs for you. It mirrors the Python SDK: same method names, same behavior.
- Source and issues: github.com/Bria-AI/bria-client (MIT)
- Node.js 20 or newer. No runtime dependencies: it uses the built-in
fetch,FormDataandBlob. - Ships ESM and CommonJS builds with type declarations.
- Covers every v2 endpoint. Legacy
/v1endpoints are not reachable through the SDK; call them withfetch.
npm install @bria-ai/client
export BRIA_API_TOKEN="<your-bria-api-token>"The client reads BRIA_API_TOKEN from the environment. You can also pass apiToken to the constructor, or override it per call with apiToken in the options of any method. Get a token from the Bria Console.
import { BriaClient, Image } from "@bria-ai/client";
const client = new BriaClient();
const response = await client.run("image/edit/remove_background", {
image: new Image("https://labs-assets.bria.ai/sandbox-example-inputs/remove_background_example.jpg").asBriaApiInput,
});
console.log(response.status); // COMPLETED
console.log(response.result?.image_url); // URL of the cutoutThe first argument is the v2 path without the /v2/ prefix, exactly as it appears in the API reference: image/generate, image/edit/remove_background, video/edit/remove_background, and so on.
JavaScript has no synchronous HTTP, so there is a single BriaClient and every method returns a Promise. Where the Python SDK offers BriaSyncClient and BriaAsyncClient, here you await (or run many calls with Promise.all).
Bria's v2 endpoints are asynchronous by default. The SDK gives you three ways to work with that:
| Method | What it does | Returns |
|---|---|---|
client.run(endpoint, payload, options?) | Sends the request with sync: true and resolves when the result is ready. Simplest for interactive use. | BriaResponse with result |
client.submit(endpoint, payload, options?) | Sends the request with sync: false and resolves immediately. Pass webhookUrl in the options to be called back. | BriaResponse with requestId and statusUrl |
client.poll(responseOrRequestId, options?) | Polls the status endpoint until the job finishes. interval and timeout are in seconds (defaults 1 and 60). Throws when the timeout elapses. | BriaResponse with result |
Do not put sync in your payload; run and submit set it for you and throw if you pass it.
// Submit now, collect later
const job = await client.submit("video/segment/mask_by_prompt", { video: videoUrl, prompt: "the red car" });
console.log("submitted:", job.requestId);
const final = await client.poll(job, { interval: 2, timeout: 600 });
console.log(final.result?.video_url);client.status(requestId) returns just the current status (IN_PROGRESS, COMPLETED, ERROR, UNKNOWN) if you manage polling yourself. Every method also accepts an AbortSignal as signal to cancel a call.
Every method returns a BriaResponse:
| Field | Description |
|---|---|
requestId | Job identifier; also the deduplication key for webhooks |
status | IN_PROGRESS, COMPLETED, ERROR or UNKNOWN (the Status enum) |
result | Endpoint result as an open object, null until the job completes: result?.image_url, result?.video_url, result?.seed, result?.structured_prompt |
error | code, message, details when the request failed, otherwise null |
statusUrl | Polling URL for async jobs |
Pass raiseForStatus: true to turn API errors into a thrown BriaException instead of inspecting response.error. poll does this by default.
import { BriaClient, BriaException } from "@bria-ai/client";
const client = new BriaClient();
try {
const response = await client.run("image/generate", { prompt: "a matte black travel mug" }, { raiseForStatus: true });
console.log(response.result?.image_url);
} catch (err) {
if (err instanceof BriaException) console.error("Bria API error:", err.code, err.message, err.details);
else throw err;
}Requests time out after 30 seconds by default (requestTimeout, in seconds, on the constructor) and throw a BriaException with code 408. GET requests, including polling, are retried on 429, 502, 503, 504 and on network errors (3 attempts with exponential backoff, honoring Retry-After). run and submit are never retried, so a job is never started twice; retry those yourself only when you know the submission did not go through.
Image turns any of these into the string the API expects, either the URL as-is or a raw base64 payload without the data: prefix:
import { readFile } from "node:fs/promises";
import { Image } from "@bria-ai/client";
new Image("https://example.com/photo.jpg").asBriaApiInput; // public URL, passed through
new Image("./photo.png").asBriaApiInput; // local path, encoded to base64
new Image(await readFile("./photo.png")).asBriaApiInput; // Buffer or Uint8Array
(await Image.fromBlob(new Blob([await readFile("./photo.png")]))).asBriaApiInput; // Blob, e.g. a browser FileThe client also drops null and undefined values from payloads, so optional parameters can be left unset without being sent.
Instead of polling, ask Bria to POST the result to your server when the job completes. webhookUrl is an option of submit, not part of the payload:
const job = await client.submit(
"image/generate",
{ prompt: "a serene mountain landscape at dawn" },
{ webhookUrl: "https://your-app.example.com/api/bria/webhook" },
);Verify every delivery with the bundled HMAC-SHA256 helper before acting on it:
import { verifyWebhookSignature } from "@bria-ai/client";
// Any framework: pass the raw request body and the three Bria-Webhook-* headers.
function isValid(rawBody: string, headers: Record<string, string>): boolean {
return verifyWebhookSignature({
payload: rawBody,
webhookId: headers["bria-webhook-id"] ?? "",
timestamp: headers["bria-webhook-timestamp"] ?? "",
signatureHeader: headers["bria-webhook-signature"] ?? "",
apiToken: process.env.BRIA_API_TOKEN ?? "",
});
}See webhooks for the delivery contract, retries and a complete Express receiver.
Video endpoints take a hosted URL. client.upload() wraps the Video Upload Service: it requests a presigned upload, uploads the file, and returns a URL that stays valid for about 24 hours. Pass the file's MIME type; only video/* types are accepted.
const fileUrl = await client.upload("path/to/video.mp4", "video/mp4");
const job = await client.submit("video/edit/remove_background", { video: fileUrl });
const result = await client.poll(job, { interval: 5, timeout: 600 });
console.log(result.result?.video_url);upload also takes a Buffer, Uint8Array or Blob instead of a path. Treat the returned URL as a secret: anyone holding it can download the file.
Submit everything first, then poll. Promise.all runs the calls concurrently on one event loop:
import { BriaClient, Image } from "@bria-ai/client";
const client = new BriaClient();
const paths = ["sku-1.jpg", "sku-2.jpg", "sku-3.jpg"];
const jobs = await Promise.all(
paths.map((p) => client.submit("image/edit/remove_background", { image: new Image(p).asBriaApiInput })),
);
const results = await Promise.all(jobs.map((job) => client.poll(job, { timeout: 120 })));
for (const [i, r] of results.entries()) console.log(paths[i], r.result?.image_url);Stay within your plan's rate limits: cap how many jobs you submit at once.
| Setting | Constructor option | Environment variable | Default |
|---|---|---|---|
| API token | apiToken | BRIA_API_TOKEN | required |
| Base URL | baseUrl | BRIA_BASE_URL | https://engine.prod.bria-api.com |
| Request timeout | requestTimeout (seconds) | 30 | |
| Retries | retry ({ total, backoffFactor }) | { total: 3, backoffFactor: 2 } | |
| Extra headers | defaultHeaders | none |
Contributions and bug reports are welcome on GitHub; the repository README covers the development setup.