ENGINEERINGJuly 19, 2026 · 12 min

Build a Bitfinex lending bot in Python

The Bitfinex API is genuinely pleasant to automate against, and a minimal lending bot fits in about sixty lines of Python with no dependencies beyond requests. This tutorial builds one for real: scoped key, rate polling, signed offer placement, a naive strategy loop. At the end, an honest section on the gap between these sixty lines and a bot you would trust with a real balance — because that gap, not the API calls, is the actual work.

What we’re building

A bot that watches the USD funding market, keeps one offer resting 10% above the current Flash Return Rate (FRR), and lets borrower demand come to it. That single behavior — always resting an offer above market — is the core of most funding strategies, because rate spikes are too short to chase manually.

Prerequisites: Python 3.10+, pip install requests, a Bitfinex account, and $150+ in the funding wallet (the exchange minimum per offer). Everything else is below.

Step 0: a key that can’t hurt you

Create the API key on Bitfinex (API Keys → Create New Key) with exactly these scopes: Margin Funding read + write, Wallets read, Account Info read. Leave withdrawals and trading disabled — a lending bot never needs them, and a scoped key means a leaked key can misprice your lending but cannot drain your account.

Full permission-by-permission detail is in our Bitfinex API guide for lenders; the short version is that everything in this post works with the scopes above and nothing more.

Step 1: read the market (no key needed)

Rates are public. The funding ticker returns the FRR as a daily rate at index 0 — multiply by 365 for APR. This loop is the bot’s eyes:

Run it and you have a live rate feed. Two conventions to internalize now: every Bitfinex rate is daily (0.0003 ≈ 10.95% APR), and every rate is gross — the exchange keeps 15% of earned interest, so multiply by 0.85 for what lands in your wallet.

                
                  import requests, time

PUB = "https://api-pub.bitfinex.com"

def frr_apr(symbol: str = "fUSD") -> float:
    ticker = requests.get(f"{PUB}/v2/ticker/{symbol}", timeout=10).json()
    return ticker[0] * 365 * 100  # index 0 = FRR, a daily rate

while True:
    print(f"FRR now: {frr_apr():.2f}% APR")
    time.sleep(300)  # poll every 5 minutes — stay well inside rate limits
                
              

Step 2: place a signed offer

Writes require authentication: HMAC-SHA384 over the path, a strictly-increasing nonce, and the JSON body. This is the entire auth layer — no SDK required:

The period is the loan term in days (2–120). Short periods keep you liquid; long ones lock the rate. Amount is a string, minimum $150 equivalent. If you get a nonce: small error, another process used the key — one key per process, always.

                
                  import hashlib, hmac, json, time, requests

KEY, SECRET = "your-key", "your-secret"
BASE = "https://api.bitfinex.com"

def auth_post(path: str, body: dict):
    nonce = str(int(time.time() * 1_000_000))  # must strictly increase per key
    raw = f"/api/{path}{nonce}{json.dumps(body)}"
    sig = hmac.new(SECRET.encode(), raw.encode(), hashlib.sha384).hexdigest()
    return requests.post(f"{BASE}/{path}", data=json.dumps(body), timeout=15, headers={
        "bfx-nonce": nonce, "bfx-apikey": KEY, "bfx-signature": sig,
        "content-type": "application/json",
    }).json()

def place_offer(rate_daily: float, amount: str = "150", period: int = 2):
    return auth_post("v2/auth/w/funding/offer/submit", {
        "type": "LIMIT", "symbol": "fUSD",
        "amount": amount, "rate": f"{rate_daily:.8f}", "period": period,
    })
                
              

Step 3: the strategy loop

Now the actual bot: if nothing is resting, place an offer 10% above the current FRR and wait. Borrower demand fills it during rate pushes; otherwise it sits. This is a crude version of a percentile floor — a fixed markup instead of a regime-aware one:

That’s a functioning lending bot. It will genuinely earn funding interest, and on quiet weeks it will not embarrass you. It is also, deliberately, the beginning of the story rather than the end.

                
                  def tick():
    apr = frr_apr()
    # naive floor strategy: rest 10% above the current FRR and wait to be hit
    target_daily = (apr * 1.10) / 365 / 100
    offers = auth_post("v2/auth/r/funding/offers/fUSD", {})
    if not offers:                      # nothing resting -> place one
        place_offer(target_daily)
    # a real bot also reprices stale offers, sizes per wallet balance,
    # handles partial fills, retries 429s, and survives restarts

while True:
    tick()
    time.sleep(600)
                
              

What these 60 lines don’t do

The honest list — each item is where hobby bots quietly die:

  • Strategy. "FRR + 10%" is arbitrary. Should the floor be the 96th percentile of the last 60 days? Should you lock 120 days when rates are regime-high? Answering that well requires replaying rules against years of data — this is the difference between a script and a strategy.
  • Repricing. A resting offer goes stale as the market moves. Production bots continuously reprice against the current book without churning fills away.
  • State and restarts. Crash mid-loop and restart: what offers are yours? What filled while you were down? Reconciling exchange state against intent is most of a real bot’s code.
  • Failure handling. 429 rate limits, 5xx responses, WebSocket drops, nonce collisions, partial fills, loans returned early — each needs a decision, not an unhandled exception at 3 a.m.
  • Accounting. Your true yield lives in the ledger (interest credits minus the 15% fee), not in the rate you offered. Reconciling per-payment ledger entries is what makes earnings and tax numbers real.

Tutorial FAQ

Is it legal/allowed to run a bot on Bitfinex?

Yes — the API exists for this, and funding automation is an explicitly supported use. Respect the per-endpoint rate limits (HTTP 429 means back off) and keep one API key per process.

Should I use the official bfxapi library instead of raw requests?

For production, probably yes — it maintains the signing, reconnection, and WebSocket plumbing for you. This tutorial hand-rolls the auth so you can see exactly what happens on the wire; the concepts transfer directly.

How much can a bot like this earn?

Whatever the funding market pays — historically ~8–12% APR gross for USD in calm regimes, more during demand spikes, minus the 15% exchange fee. The bot doesn’t create yield; it captures market yield more reliably than manual lending. See our three-year data study for the honest distribution.

Build it, or skip to the strategy part

Everything hard about this bot is strategy and reliability, not API calls. Stratum is that production layer: 11 backtested strategies, repricing, reconciliation, and reporting behind the same withdraw-disabled key scoping — flat fee, no cut of earnings. Or take the code above and own the whole stack; this page stays here either way.

See the backtested strategiesFull Bitfinex API guide