Skip to main content

fluidghost

Official Python client for the FluidGhost API.

FluidGhost turns one photo or video into a set of distinct variants — each a fresh, standalone capture with its own metadata, its own pixels, and a device-authentic filename. This SDK spoofs media with your API key, over the submit → poll → download loop (a single photo, a video, or a whole carousel), and inspects metadata (see Inspect metadata).

Install

pip install fluidghost

Authentication

Mint an API key in the dashboard (Settings → API keys at https://ghost.fluidvip.com). Keep it server-side — never ship it in client code. A secret API key can submit jobs, read and download their results, and inspect metadata (management keys only provision).

Spoof a photo or video

If your API key has a saved preset bound to it (pick it when you create the key in the dashboard), the key already carries the settings — the whole call is just the image:

job = fg.run(image="photo.jpg")

Otherwise name the settings on the call, as below. Note that recipe_id, recipe_config and preset_id override the key's bound preset.

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

import os
from fluidghost import FluidGhost

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

job = fg.run(
    image="photo.jpg",                 # or a video: "clip.mp4"
    recipe_id="full-refresh",
    copies=3,
    options={"noise": 35, "ultraSafe": True},
    on_poll=lambda j: print(j["status"], j.get("progress")),
)

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)
    print(v.get("fileName"), "risk", v.get("detectionRisk"), v.get("riskBand"))

You can pass in-memory bytes instead of a path with a (filename, bytes) tuple:

fg.create_job(image=("photo.jpg", my_bytes), recipe_id="full-refresh")

A carousel is a set of photos posted together. spoof_carousel() 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.

jobs = fg.spoof_carousel(
    ["slide1.jpg", "slide2.jpg", "slide3.jpg"],
    recipe_id="full-refresh",
)

for job in jobs:
    v = job["variants"][0]
    data = fg.download_variant(job["jobId"], v["index"])
    with open(v["fileName"], "wb") as f:
        f.write(data)

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

Inspect metadata

Four read-only calls read metadata without creating a job. They need the inspect:read scope (secret API keys have it by default) and share one rate limit of 60 requests per minute per owner (a 429 raises FluidGhostError with retry_after). Every file argument takes a path or a (filename, bytes) tuple, like run().

# Metadata of an uploaded photo or video, grouped by container.  POST /api/inspect
meta = fg.inspect("photo.jpg")

# Deep forensic verdict (metadata, container, C2PA).  POST /api/inspect/forensics
# Fail-open: a missing engine returns {"status": "unavailable"} or {"status": "error"}.
report = fg.inspect_forensics("photo.jpg")

# Perceptual (Meta-PDQ) compare of an original and a variant.  POST /api/inspect/compare
cmp = fg.inspect_compare("original.jpg", "variant.jpg")
print(cmp["detectionRisk"], cmp["band"], cmp["matchThreshold"])

# Metadata of a finished variant, read server-side (does not consume the one-time
# download link).  GET /api/inspect/result/{jobId}/{index}
variant_meta = fg.inspect_result(job["jobId"], 0)

Settings

Provide exactly one recipe source (or bind a default preset to your key and omit all three; any of the three you do send overrides the key's preset):

Argument Meaning
recipe_id A bundled starter, e.g. "full-refresh".
preset_id A saved preset created in the dashboard (e.g. "sys:full-refresh").
recipe_config A full recipe graph (advanced).
copies Number of variants, 1–50.
strength Global strength 0–100.
options Extra knobs merged onto the recipe, e.g. {"noise": 35, "ultraSafe": True}.

Errors

Every non-2xx response raises FluidGhostError, mirroring the API's {error, message, details}:

from fluidghost import FluidGhost, FluidGhostError

try:
    fg.run(image="photo.jpg", recipe_id="full-refresh", copies=3)
except FluidGhostError as e:
    print(e.status, e.code, e.details)
    if e.code == "rate_limited":
        time.sleep(e.retry_after or 5)
    if e.code == "insufficient_balance":
        ...  # top up in the dashboard

Configuration

FluidGhost(
    api_key="fg_sk_live_…",
    base_url="https://api-ghost.fluidvip.com",  # default; override for a proxy
    timeout=60.0,
)

License

MIT

Metadata

Release files for fluidghost 2.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for fluidghost 2.1.0
File Size Uploaded Provenance Actions
fluidghost-2.1.0.tar.gz 9.5 kB Without provenance Details for fluidghost-2.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for fluidghost 2.1.0
File Interpreter ABI Platform Actions
fluidghost-2.1.0-py3-none-any.whl Python 3 none any Details for fluidghost-2.1.0-py3-none-any.whl

Total release size: 18.7 kB

Release files / fluidghost-2.1.0.tar.gz

Download URL fluidghost-2.1.0.tar.gz
Size 9.5 kB
Tags Source
SHA-256 checksum
How to use checksums
5d116708fdb788758e6b7ba62ad3f3cfbb5f07bdc8a9336406ad1a14570a9aaf
BLAKE2b-256 checksum
How to use checksums
c67e124d6a39147d5253119e6a0326872e8fac42c85a17c306d3752c60ce204c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.11

Release files / fluidghost-2.1.0-py3-none-any.whl

Download URL fluidghost-2.1.0-py3-none-any.whl
Size 9.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
fd13ade86e1de4cd802e6dd65bb9c4043e0e4cc1093bbef0c7f3ebc1258086d8
BLAKE2b-256 checksum
How to use checksums
24e62dc0ce1bb6b2f393bf5c3b5a6c06c6a437570d4408a599fc0bf0b5637462
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.11

Security for fluidghost 2.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page