marginfuse
Server-side SDK for MarginFuse: profitability guardrails for AI SaaS. Connect revenue to per-request AI cost, see gross margin per customer, and stop loss-making requests before they run.
- Metadata only, by construction. The event shape has no field for prompts or responses, so they cannot be sent. Not a policy, an absence.
- Never breaks your app. It does not raise into your code, and it does not block your request on MarginFuse being up. If MarginFuse is unreachable, your requests proceed unchanged.
- Zero dependencies. Standard library only, Python 3.9+.
Server side only. This SDK carries a secret API key. Never ship it in a desktop app, a mobile app, or anything else a user can read.
Install
pip install marginfuse
Track an AI call
Monitoring. One call after each AI request, metadata only.
import os
from marginfuse import MarginFuse, Usage
mf = MarginFuse(api_key=os.environ["MARGINFUSE_KEY"])
r = client.chat.completions.create(model="gpt-4.1", messages=messages)
mf.track(
customer_id="cus_8x2m91", # your Stripe customer id, or your own
feature="ai_chat",
provider="openai",
model="gpt-4.1",
usage=Usage(
input_tokens=r.usage.prompt_tokens,
output_tokens=r.usage.completion_tokens,
),
)
track() returns immediately and sends on a background thread with retries. In
a script, a Celery task or a Lambda handler, call mf.flush() before the
process exits, or the last events go with it.
with MarginFuse(api_key=os.environ["MARGINFUSE_KEY"]) as mf:
... # closing flushes
Guard a call
Protection. Ask before the call runs, and act on the answer.
from marginfuse import MarginFuse, ProviderCall, Usage
def run(decision):
# decision.model is the one to call: a downgrade verdict changes it.
r = client.chat.completions.create(model=decision.model, messages=messages)
return ProviderCall(
result=r,
usage=Usage(
input_tokens=r.usage.prompt_tokens,
output_tokens=r.usage.completion_tokens,
),
)
out = mf.guard(run, customer_id="cus_8x2m91", feature="ai_chat", provider="openai", model="gpt-4.1")
if out.kind == "completed":
use(out.result)
elif out.kind == "topup_required":
show_topup(out.decision.topup_context)
else:
show_limit_reached()
One call does the whole loop: ask, run with the resolved model, report the real cost, acknowledge what your application did.
Why a callback and not a context manager
A with block always runs its body. Enforcement would then depend on you
remembering to check a flag, and forgetting once means a blocked request reaches
the provider anyway. With a callback that is structurally impossible: when the
verdict is block, your function is never called.
OpenRouter and other gateways
Gateways report the real cost of every call. Forward it and your figures are exact instead of estimated.
from marginfuse import from_openrouter
r = client.chat.completions.create(model="anthropic/claude-sonnet-4.5", messages=messages)
mf.track(
customer_id="cus_8x2m91",
feature="ai_chat",
provider="openrouter",
model="anthropic/claude-sonnet-4.5",
**from_openrouter(r.usage),
)
Use the helper rather than mapping the fields yourself. OpenRouter's
prompt_tokens already includes cached reads and cache writes, which MarginFuse
prices separately, so passing it through directly charges every cached token
twice at the full input rate. The helper also formats the cost as a decimal
string, because str(1.2e-07) produces exponent notation and the API rejects
that.
Configuration
mf = MarginFuse(
api_key=os.environ["MARGINFUSE_KEY"],
base_url="https://api.marginfuse.com", # your own deployment in dev
timeout=1.5, # decide() budget before failing open
on_error=lambda err, ctx: log.warning("marginfuse %s: %s", ctx, err),
)
on_error is the only place transport failures surface. The SDK swallows them
so they cannot become your outage; without the hook they are silent.
Async applications
This client is synchronous and does no I/O on the calling thread except during
decide() and guard(). In an async application, keep the event loop free by
running those in a worker thread:
decision = await asyncio.to_thread(mf.decide, customer_id=cid, provider="openai", model="gpt-4.1")
track() and acknowledge() already return immediately, so they are safe to
call directly from async code.
What it sends
Everything, and nothing else:
event_id customer_id feature provider model requested_model
usage(input_tokens, output_tokens, cached_input_tokens,
cache_creation_tokens, images, audio_seconds)
cost_usd occurred_at outcome decision_id retry_of_event_id corrects_event_id
There is no field for message content anywhere in the wire types. The conformance suite checks this against the bytes that actually leave the process, on every scenario.
Conformance
This SDK is verified against marginfuse/sdk-contract, the same contract every MarginFuse SDK in every language is held to. It is a submodule here, so the pinned commit records exactly which contract a release passed.
git clone --recurse-submodules https://github.com/marginfuse/marginfuse-python
cd marginfuse-python
uv sync
uv run pytest # unit tests, plus the shared gateway vectors
uv run --directory contract/harness npm install
npm --prefix contract/harness run conformance python
Links
- MarginFuse, product and pricing
- Documentation
- API reference
- Security policy
- Contributing
MIT, Pemira Labs.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file marginfuse-0.1.0.tar.gz.
File metadata
- Download URL: marginfuse-0.1.0.tar.gz
- Upload date:
- Size: 75.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b457bc03650c8434618cf2bfe3cd061bc52e6e2e4c08138623cf4514d12265e1
|
|
| MD5 |
366cb7afc900b11f3556bf936562f1f6
|
|
| BLAKE2b-256 |
162d390d58900fcb5608dbd7053355244f077a7ecf8c2d152a17dabd1c64c1e8
|
Provenance
The following attestation bundles were made for marginfuse-0.1.0.tar.gz:
Publisher:
release.yml on marginfuse/marginfuse-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
marginfuse-0.1.0.tar.gz -
Subject digest:
b457bc03650c8434618cf2bfe3cd061bc52e6e2e4c08138623cf4514d12265e1 - Sigstore transparency entry: 2679658652
- Sigstore integration time:
-
Permalink:
marginfuse/marginfuse-python@3dbd30c02ffa3f8e0845d56d6ecc8ddaa062e250 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/marginfuse
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@3dbd30c02ffa3f8e0845d56d6ecc8ddaa062e250 -
Trigger Event:
push
-
Statement type:
File details
Details for the file marginfuse-0.1.0-py3-none-any.whl.
File metadata
- Download URL: marginfuse-0.1.0-py3-none-any.whl
- Upload date:
- Size: 12.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d953cec9c1d3f8f8b78fe03e7cf283060ece227d2fb55aeac7420d8ff5b3c698
|
|
| MD5 |
7f8554182b44e9b196a2c9aa8e6e5263
|
|
| BLAKE2b-256 |
0838d0a7fb55c517b4b1c3cc13a23d8c4d5dc5afbca42456baa65371d6a91f7e
|
Provenance
The following attestation bundles were made for marginfuse-0.1.0-py3-none-any.whl:
Publisher:
release.yml on marginfuse/marginfuse-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
marginfuse-0.1.0-py3-none-any.whl -
Subject digest:
d953cec9c1d3f8f8b78fe03e7cf283060ece227d2fb55aeac7420d8ff5b3c698 - Sigstore transparency entry: 2679658690
- Sigstore integration time:
-
Permalink:
marginfuse/marginfuse-python@3dbd30c02ffa3f8e0845d56d6ecc8ddaa062e250 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/marginfuse
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@3dbd30c02ffa3f8e0845d56d6ecc8ddaa062e250 -
Trigger Event:
push
-
Statement type: