Skip to main content

fixedfloat-client

PyPI version Python versions License: MIT

Typed Python client for the FixedFloat API v2. Sync and async clients cover currencies, fixed and float quotes, the public XML rate file, orders, polling, emergency choices, email notifications, and deposit QR codes.

Official API reference: https://ff.io/api

Contents

Features

  • Every public v2 method: ccies, price, create, order, emergency, setEmail, qr, plus GET /rates/{fixed|float}.xml.
  • HMAC-SHA256 X-API-SIGN over the compact JSON body.
  • Fixed and float rates, both directions, ccies and usd quote flags, and afftax.
  • Shorthand quotes ("0.1 ETH", "BTC" and "ETH", "0.1 BTC").
  • Parsed XML rates with pair filters.
  • wait_for_order with timeout, interval, target statuses, and a stop callback.
  • Retries with exponential backoff for network errors, timeouts, HTTP 429 and 5xx. POST /create is not retried.
  • Proxy URL, custom API base URL, custom rates origin, timeout, and extra headers.
  • Dataclasses for orders, including the refund side, emergency block, and transaction confirmations.
  • FixedFloat (sync, httpx.Client) and AsyncFixedFloat (httpx.AsyncClient) with the same methods.
  • Error subclasses for API, HTTP, auth, rate limit, network, and timeout failures.

Comparison

Capability fixedfloat-client FixedFloatApi fixedfloat-py fixedfloat 0.2
Currencies, price, create, order, emergency yes yes yes partial
Email and QR yes yes no no
Parsed XML rates yes no no no
ccies / usd quote flags yes no no no
Order polling yes no no no
Retries and proxy yes no no no
Async client yes no no no
Typed models and error classes yes no no no

The JavaScript package of the same name is in fixedfloat/js. npm libraries ff-io, fixedfloat, and fixedfloat-api wrap a subset of the POST methods. ff-io also downloads the XML file as a string and can set an email. None of them poll, retry, or parse the rate file into rows.

Installation

pip install fixedfloat-client

Python 3.10 or newer. The only runtime dependency is httpx.

Create an API key and secret in the FixedFloat account under API management. The secret is the HMAC key.

Configuration

from fixedfloat import FixedFloat

ff = FixedFloat(
    api_key="your-key",
    api_secret="your-secret",
    base_url="https://ff.io/api/v2",
    rates_base_url="https://ff.io",
    timeout=15.0,
    retries=2,
    retry_delay=0.2,
    proxy="http://user:pass@127.0.0.1:8080",
    headers={"X-Request-Id": "checkout-1"},
)
Argument Default Description
api_key None Sent as X-API-KEY.
api_secret None HMAC-SHA256 key for X-API-SIGN. The message is the raw JSON body.
base_url https://ff.io/api/v2 JSON API origin and prefix.
rates_base_url https://ff.io Origin for /rates/float.xml and /rates/fixed.xml.
timeout 15 Per-request timeout in seconds.
retries 2 Extra attempts after a transient failure. Business errors are not retried. POST /create is never retried.
retry_delay 0.2 Base backoff in seconds. Attempt n waits retry_delay * 2**n.
proxy None Proxy URL passed to httpx.
headers None Extra headers on signed JSON calls. Auth headers overwrite collisions.

Use the client as a context manager so the connection pool closes. close() does the same thing.

Price and create requests always include the package referral code. afftax is optional and only changes the affiliate percentage when you pass it.

AsyncFixedFloat takes the same arguments. Close it with async with or await client.aclose().

Quick start

from fixedfloat import FixedFloat

with FixedFloat(api_key="key", api_secret="secret") as ff:
    currencies = ff.get_currencies()
    print(len(currencies))

    price = ff.get_price("BTC", "ETH", 0.01, direction="from", order_type="float")
    print(price.from_side.amount, "->", price.to_side.amount, price.to_side.code)

This does not create an order.

Currencies

POST /ccies. No body.

currencies = ff.get_currencies()
# alias: ff.ccies()

from fixedfloat import find_currency

usdt = find_currency(currencies, "USDTTRC")
if usdt and usdt.tag:
    print("Destination field:", usdt.tag)
print(usdt.recv, usdt.send, usdt.contract, usdt.priority)

recv and send are 1 or 0. Booleans from the API are normalized with int.

Quotes

POST /price.

Argument Meaning
from_ccy, to_ccy FixedFloat codes such as BTC, ETH, USDTTRC.
amount Coins to send or to receive, depending on direction.
direction "from" (send this amount) or "to" (receive this amount). Default "from".
order_type "fixed" or "float". Default "float".
ccies When true, PriceResult.ccies lists send/receive availability.
usd When true, out-of-limit amounts are adjusted using the USD equivalent.
afftax Optional affiliate percentage.
quote = ff.get_price(
    "BTC",
    "USDTTRC",
    0.05,
    direction="from",
    order_type="float",
    ccies=True,
    usd=True,
    afftax=0.1,
)
print(quote.from_side.min, quote.from_side.max, quote.from_side.usd, quote.from_side.btc)
print(quote.to_side.amount, quote.to_side.rate, quote.to_side.precision)
print(quote.errors)  # [] when an order can be created
print(quote.exchangeable)
print(quote.ccies[0].code if quote.ccies else None)

ff.price("ETH", "BTC", 0.2, direction="to", order_type="fixed")

Shorthand:

sent = ff.get_price_shorthand("0.1 ETH", "BTC")  # direction "from"
wanted = ff.get_price_shorthand("ETH", "0.01 BTC", "fixed")  # direction "to"

get_price returns the quote even when errors is non-empty. get_price_or_raise calls assert_quote_ok and raises FixedFloatError with code 0.

from fixedfloat import assert_quote_ok

quote = ff.get_price("BTC", "ETH", 0.01)
assert_quote_ok(quote)
# or
ff.get_price_or_raise("BTC", "ETH", 0.01)

Download the whole board with the XML export, not with one quote per pair.

XML rates

GET https://ff.io/rates/float.xml and GET https://ff.io/rates/fixed.xml. No API key.

xml = ff.get_rates_xml("float")
board = ff.get_rates(order_type="float")
pair = ff.get_rates(order_type="fixed", from_ccy="BTC", to_ccy="ETH")
row = pair[0]
print(row.in_amount, row.from_ccy, "->", row.out_amount, row.to_ccy)
print(row.to_fee, row.min_amount, row.max_amount, row.amount)
Attribute XML tag Meaning
from_ccy, to_ccy from, to Currency codes.
in_amount, out_amount in, out Rate base and the amount received for that base.
amount amount Reserve indicator. Not the maximum exchange size.
to_fee tofee Network fee excluded from the rate.
min_amount, max_amount minamount, maxamount Limits, with the coin code in the string.

parse_rates_xml and filter_rates work on a document you already have.

Create an order

POST /create. Costs 50 weight units. Not retried.

order = ff.create_order(
    "BTC",
    "USDTTRC",
    0.02,
    "TAzsQ9Gx8eqFNFSKbeXrbi45CuVPHzA8wr",
    direction="from",
    order_type="float",
    tag="12345",
    afftax=0.1,
)

ff.create(
    "ETH",
    "BTC",
    0.001,
    "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
    direction="to",
    order_type="fixed",
)

tag is the official memo field. extra_id is still accepted and is sent as tag and as extraId. refund_address and refund_extra_id are forwarded when set. A memo can also be appended as address:memo.

Store order.id and order.token. The id is 6 characters. Later calls need both. There is no order-list method.

print(order.id, order.token, order.type, order.status, order.status_text)
print(order.from_side.address, order.from_side.tag, order.from_side.address_mix)
print(order.from_side.req_confirmations, order.time.expiration, order.time.left)
print(order.to_side.amount, order.back, order.emergency)

The deposit side is from_side and the payout side is to_side (from is a Python keyword).

Read an order

current = ff.get_order(order.id, order.token)
# alias: ff.order(order.id, order.token)

print(current.status, current.status_text)
print(ff.status_text(current.status))
tx = current.from_side.tx
if tx:
    print(tx.id, tx.confirmations, tx.time_block, tx.fee, tx.ccyfee)
Status status_text
NEW New order
PENDING Transaction received, pending confirmation
EXCHANGE Transaction confirmed, exchange in progress
WITHDRAW Sending funds
DONE Order completed
EXPIRED Order expired
EMERGENCY Emergency, customer choice required

Wait for an order

done = ff.wait_for_order(
    order.id,
    order.token,
    statuses=["DONE", "EXPIRED", "EMERGENCY"],  # default
    timeout=30 * 60,  # seconds, default
    interval=5,  # seconds, default
    should_stop=lambda: False,
)

EMERGENCY is in the default set so you can submit a choice and wait again for DONE. A deadline raises FixedFloatTimeoutError with retryable false. If should_stop() becomes true, the error message is The order wait was aborted.

Emergency choice

POST /emergency, for orders already in EMERGENCY.

choice Meaning
EXCHANGE Continue at the market rate when the choice is made or the order is corrected.
REFUND Refund minus the miner fee. address is required. tag is the memo when the network needs one.

order.emergency.status may include EXPIRED, LESS, MORE, and LIMIT. choice is NONE, EXCHANGE, or REFUND. repeat is true when a second deposit arrived.

ff.set_emergency(order.id, order.token, "EXCHANGE")

ff.emergency(
    order.id,
    order.token,
    "REFUND",
    address="bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
    tag="optional-memo",
)

extra_id is still accepted. Success data is True.

Email notifications

ff.set_email(order.id, order.token, "user@example.com")
ff.set_email_notification(order.id, order.token, "user@example.com")
ff.subscribe(order.id, order.token, "user@example.com")

POST /setEmail. Success data is True. This is the only notification channel. FixedFloat does not document webhooks.

QR codes

qr = ff.get_qr(order.id, order.token)
# aliases: ff.qr, ff.get_qr_codes

print(qr.title, qr.src[:32], qr.checked)
for code in qr.codes:
    print(code.title, code.checked, len(code.src))

src is the checked image, or the first image. Each QrCode has title, src (data:image/png;base64,...), and checked.

Raw requests

data = ff.raw_request("/order", {"id": order.id, "token": order.token})
ff.raw_request("future", {"a": 1}, idempotent=False)

Returns the envelope data field. Paths other than /create are retried unless idempotent is false.

Async client

import asyncio
from fixedfloat import AsyncFixedFloat


async def main() -> None:
    async with AsyncFixedFloat(api_key="key", api_secret="secret") as ff:
        price = await ff.get_price("BTC", "ETH", 0.01)
        rates = await ff.get_rates(from_ccy="BTC", to_ccy="ETH")
        order = await ff.wait_for_order("ABCDEF", "token", timeout=60, interval=5)
        print(price.to_side.amount, rates[0].out_amount, order.status)


asyncio.run(main())

Every sync method exists as a coroutine with the same arguments, including ccies, price, create, order, emergency, subscribe, qr, get_rates_xml, and raw_request. status_text stays synchronous.

Error handling

from fixedfloat import (
    FixedFloatApiError,
    FixedFloatAuthError,
    FixedFloatError,
    FixedFloatHttpError,
    FixedFloatNetworkError,
    FixedFloatRateLimitError,
    FixedFloatTimeoutError,
)

try:
    ff.get_order("ABCDEF", "token")
except FixedFloatRateLimitError as exc:
    # HTTP 429. create costs 50 weight, everything else costs 1. Cap is 250/minute.
    print(exc.code, exc.status, exc)
except FixedFloatAuthError as exc:
    # HTTP 401 or 403.
    print(exc)
except FixedFloatApiError as exc:
    # Envelope code != 0. exc.code, exc.message, exc.data.
    print(exc.code, exc.data)
except FixedFloatTimeoutError as exc:
    print(exc.retryable)
except FixedFloatNetworkError as exc:
    print(exc)
except FixedFloatHttpError as exc:
    print(exc.status, exc.retryable)
except FixedFloatError as exc:
    print(exc.code, exc)

str(exc) is [code] message when a code is present.

FixedFloat does not publish a stable numeric catalog. code == 0 is success. Any other code is a failure and msg is the reason. Match on the message and on the quote error strings below, not on invented numbers.

Order and quote codes

Quote errors (the HTTP call still succeeds):

Code Meaning
MAINTENANCE_FROM Currency being sent is under maintenance.
MAINTENANCE_TO Currency being received is under maintenance.
OFFLINE_FROM Currency being sent is offline.
OFFLINE_TO Currency being received is offline.
RESERVE_FROM Not enough reserves of the currency being sent.
RESERVE_TO Not enough reserves of the currency being received.
LIMIT_MIN Amount is below the minimum.
LIMIT_MAX Amount is above the maximum.

PRICE_ERROR_TEXT has those sentences. TERMINAL_ORDER_STATUSES is DONE, EXPIRED, EMERGENCY. ORDER_STATUS_TEXT is the status label map.

HTTP status Class Retried
401, 403 FixedFloatAuthError no
429 FixedFloatRateLimitError yes
500 and above FixedFloatApiError or FixedFloatHttpError yes
other 4xx FixedFloatHttpError no

A key that keeps passing the weight cap stays blocked longer each time.

Advanced usage

Proxy and timeouts

with FixedFloat(
    api_key="key",
    api_secret="secret",
    timeout=20,
    proxy="http://127.0.0.1:8888",
    retries=4,
    retry_delay=0.1,
    base_url="https://ff.io/api/v2",
) as ff:
    ff.get_currencies()

Retries

Network errors, HTTP timeouts, 429, and 5xx use exponential backoff. Set retries=0 to disable. create_order ignores retries so a failed response cannot be sent twice.

Sync and async together

Do not share one httpx client across threads. Construct a FixedFloat per thread, or use AsyncFixedFloat inside a single event loop. There are no callbacks. Poll, or subscribe an email with set_email.

Type hints

The package is typed for Python 3.10+. Models are dataclasses: Currency, PriceResult, PriceSide, PriceCurrency, Order, OrderSide, OrderTx, OrderTime, OrderEmergency, QrResult, QrCode, RateItem. Literals: OrderType, ExchangeDirection, EmergencyChoice, OrderStatus, PriceErrorCode.

Examples

File What it does
examples/check.py Currencies, one quote, one XML pair. Needs API keys.
examples/price.py Fixed quote, shorthand, get_price_or_raise. Needs API keys.
examples/rates.py Downloads the XML board. No API key.
examples/parse_rates.py Parses a sample document offline.
examples/async_quote.py Async quote and fixed XML pair.
examples/create_order.py Opens an order only when FIXEDFLOAT_CREATE=1.
examples/manage_order.py Reads an order, optional email, QR, emergency, and wait.
uv run python examples/parse_rates.py
uv run python examples/rates.py
FIXEDFLOAT_API_KEY=... FIXEDFLOAT_API_SECRET=... uv run python examples/check.py

FAQ

Do I need a key for rates? No. get_rates is public. /api/v2 needs a key and a secret.

Why is there no order list? The API returns an order only when you send its id and token.

Fixed or float? Fixed locks the rate (about 1% before affiliate adjustments). Float follows the market at execution (about 0.5%). Orders usually expire after 30 minutes (time.left).

Can I change the referral code? Price and create always send this package's referral code. Pass afftax when you want a different affiliate percentage on top of that.

Wrong deposit size? The order becomes EMERGENCY. Read order.emergency.status, then set_emergency with EXCHANGE or REFUND.

Contributing

cd fixedfloat/python
uv sync
uv run ruff check .
uv run pytest -m "not live"   # coverage gate is 95% lines

Live tests are skipped unless FIXEDFLOAT_API_KEY and FIXEDFLOAT_API_SECRET are set. They only call get_currencies, get_price, and get_rates.

FIXEDFLOAT_API_KEY=... FIXEDFLOAT_API_SECRET=... uv run pytest -m live

License

MIT

Metadata

Release files for fixedfloat-client 0.2.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 fixedfloat-client 0.2.0
File Size Uploaded Provenance Actions
fixedfloat_client-0.2.0.tar.gz 64.0 kB Without provenance Details for fixedfloat_client-0.2.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for fixedfloat-client 0.2.0
File Interpreter ABI Platform Actions
fixedfloat_client-0.2.0-py3-none-any.whl Python 3 none any Details for fixedfloat_client-0.2.0-py3-none-any.whl

Total release size: 84.8 kB

Release files / fixedfloat_client-0.2.0.tar.gz

Download URL fixedfloat_client-0.2.0.tar.gz
Size 64.0 kB
Tags Source
SHA-256 checksum
How to use checksums
e07c197ae81c3d0ed2fdf226fb66a060a1ea36253543028cbd4733acf4443d8e
BLAKE2b-256 checksum
How to use checksums
cf21b699b53a121d069bdede7c52eebb078c22b741fd1402530bdde97c03f01d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / fixedfloat_client-0.2.0-py3-none-any.whl

Download URL fixedfloat_client-0.2.0-py3-none-any.whl
Size 20.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d396bd55bb64f76f4e5bf9f615e5f9833e183606cdd37154aecb42568e4b61cb
BLAKE2b-256 checksum
How to use checksums
b2b08074ef7736480fb68a0031210238d020de89941ce6dcf649a51a413ba22d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for fixedfloat-client 0.2.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.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files No provenance

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