Skip to content

Official SDKs

We publish official clients so you don't have to write the HTTP, multipart, and polling yourself. Both wrap the same submit → poll → download loop and handle a single photo, a video, or a whole carousel.

LanguageInstallPackage
TypeScript / JavaScriptnpm install fluidghostnpmjs.com/package/fluidghost
Pythonpip install fluidghostpypi.org/project/fluidghost

Generate an API key in the dashboard and keep it server-side.

Spoof a photo or video

run() submits a job and waits for it to finish. Photos and videos both go through the same call — the type is detected from the file. Results are one-time links: downloadVariant streams the bytes and the object is deleted on a successful download.

ts
import { FluidGhost } from "fluidghost";
import { writeFile } from "node:fs/promises";

const fg = new FluidGhost({ apiKey: process.env.FLUIDGHOST_KEY! });

const job = await fg.run({
  image: { path: "photo.jpg" },     // or a video: { path: "clip.mp4" }
  recipeId: "full-refresh",
  copies: 3,
});

for (const v of job.variants) {
  if (v.status !== "completed") continue;
  const bytes = await fg.downloadVariant(job.jobId, v.index);
  await writeFile(v.fileName ?? `variant-${v.index}.jpg`, bytes);
}
python
import os
from fluidghost import FluidGhost

fg = FluidGhost(api_key=os.environ["FLUIDGHOST_KEY"])

job = fg.run(image="photo.jpg", recipe_id="full-refresh", copies=3)  # or "clip.mp4"

for v in job["variants"]:
    if v["status"] != "completed":
        continue
    data = fg.download_variant(job["jobId"], v["index"])
    with open(v.get("fileName") or f"variant-{v['index']}.jpg", "wb") as f:
        f.write(data)

A carousel is a set of photos posted together. spoofCarousel submits one linked job per file — sharing a session and a running filename sequence so the outputs read as one cohesive post — and waits for all of them.

ts
const jobs = await fg.spoofCarousel(
  [{ path: "slide1.jpg" }, { path: "slide2.jpg" }, { path: "slide3.jpg" }],
  { recipeId: "full-refresh" },
);
python
jobs = fg.spoof_carousel(
    ["slide1.jpg", "slide2.jpg", "slide3.jpg"],
    recipe_id="full-refresh",
)

For a fully cohesive set (the same device across every slide), pass a recipeConfig or presetId that pins a device — otherwise each slide picks its own device family.

Errors

Every non-2xx response throws / raises a FluidGhostError mirroring the API's { error, message, details } envelope — see Errors. Handle rate_limited (back off for retryAfter) and insufficient_balance (top up in the dashboard).

What a key can do

An API key is spoof-only: submit jobs, and poll/download their results. That's the whole surface these SDKs expose. See Authentication.

FluidGhost API — part of the Fluidvip ecosystem.