Python SDK

Updated

The Python package is named faxforagents and has been on PyPI since 2026-10-30. Install it with pip install faxforagents. Python 3.10 or later, two dependencies, httpx and pydantic. The REST calls further down work with httpx or requests as well, so nothing here depends on the package.

Status

Published. The package is built from the same OpenAPI document as this site and released through PyPI trusted publishing. The quickstart below is the one from the package's README. The source is at github.com/faxforagents/sdk-python and the package page is pypi.org/project/faxforagents.

Install and send with the package

Shell
pip install faxforagents            # the client
pip install "faxforagents[cli]"     # plus the faxforagents command
Python
import os
from faxforagents import Client

os.environ["FAXFORAGENTS_API_KEY"] = "ffa_test_..."      # or export it in the shell

with Client() as ffa:
    doc = ffa.documents.create(text="Hello from an agent.", filename="hello")
    print(doc.id, doc.page_count, doc.sha256[:12])

    quote = ffa.quotes.create(document_id=doc.id, to="+15550000001")
    print(quote.amount_micro, "micro, expires", quote.expires_at)

    fax = ffa.faxes.create(quote_id=quote.id)              # Idempotency-Key minted for you
    print(fax.id, fax.state)                               # queued, never delivered yet

    fax = ffa.faxes.wait(fax.id)                           # polls until a terminal state
    print(fax.state, fax.pages_sent, fax.evidence)         # delivered, 1, {...}

    for job in ffa.faxes.iter(state="failed"):             # follows next_cursor
        print(job.id, job.failure_code)

A test key is tied to the sandbox, where the destination number picks the outcome. The same calls exist in async form, with AsyncClient and await on every call.

Send a fax with httpx

Python
import httpx

API = "https://api.faxforagents.com/v1"
KEY = "ffa_test_..."  # from the dashboard, never in source control
h = {"Authorization": f"Bearer {KEY}"}

with httpx.Client(base_url=API, headers=h, timeout=30) as c:
    doc = c.post("/documents", json={"markdown": "# PO 4411\n\nPlease confirm."}).raise_for_status().json()
    quote = c.post("/fax-quotes", json={"document_id": doc["id"], "to": "+15550000001"}).raise_for_status().json()
    job = c.post("/faxes", json={"quote_id": quote["id"]}, headers={"Idempotency-Key": "po-4411"}).raise_for_status().json()
    print(job["id"], job["state"])  # fax_..., queued

Upload a PDF

Python
size = path.stat().st_size
up = c.post("/uploads", json={"filename": path.name, "size_bytes": size}).raise_for_status().json()
httpx.put(up["upload_url"], content=path.read_bytes(), timeout=120).raise_for_status()
c.post(f"/uploads/{up['id']}/complete").raise_for_status()
doc = c.post("/documents", json={"upload_id": up["id"]}).raise_for_status().json()

How do I page through jobs?

Python
cursor = None
while True:
    page = c.get("/faxes", params={"limit": 50, "cursor": cursor}).raise_for_status().json()
    for job in page["items"]:
        print(job["id"], job["state"])
    cursor = page.get("next_cursor")
    if not cursor:
        break

How do I verify a webhook?

The reference verifier is on the webhooks page. The package ships the same function as faxforagents.webhooks.verify.

Errors

Every non-2xx body is the error envelope. Read error.code and error.retryable. Retry a retryable call with the same Idempotency-Key. The errors page lists every code.