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).
- Full API docs: https://ghost.fluidvip.com/docs/
- Python 3.8+ (depends on
requests).
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")
Spoof a carousel (matched set)
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_configorpreset_idthat 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)
| 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)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| 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.