Billy billy Beta Docs

Errors & idempotency

What Billy returns when something goes wrong, and how to retry without paying a bill twice.

Error shape

Every error is JSON with a message you can log:

{
  "message": "Wallet balance is insufficient for this payment.",
  "code": "insufficient_balance"
}

Validation errors additionally carry per-field detail:

{
  "message": "The 10 Digit Account Number field is required.",
  "errors": {
    "fields.field_1": ["The 10 Digit Account Number field is required."]
  }
}

Status codes

StatusMeaningRetry?
401Missing, invalid, or revoked API key.No — fix the key.
402Insufficient float. The request was valid; you're out of money.Yes, after topping up.
404Not found, or not available to your account.No.
422Validation failed — a bad amount, a missing or malformed field.No — fix the request.
429Rate limit exceeded.Yes, after backing off.
5xxSomething broke on Billy's side.Yes, with the same idempotency key.
Treat 402 and 422 differently. They look similar and mean opposite things. A 422 will never succeed until you change the request. A 402 is a perfectly good request that will go through the moment your float is funded — surface it to whoever tops up, don't log it as a bug.

Rate limits

RequestsLimit
Authenticated (per partner)120 per minute
Unauthenticated (per IP)30 per minute

The limit is per partner, not per key — issuing more keys won't raise it. Exceeding it returns 429; back off and retry.

Idempotency

Networks fail in the worst possible way: your request arrives, Billy pays the bill, and the response is lost. You have no idea whether it worked, and retrying blind risks paying twice.

An idempotency key fixes this. Send one on every payment:

Idempotency-Key: 8f14e45f-ea28-4e1a-9a2b-1c2d3e4f5a6b

If Billy has already seen that key from your account, it returns the original payment instead of creating a new one. Retry as many times as you like — you get the same payment back every time.

Choosing keys

  • Unique per payment attempt — a UUID, or your own order id. Never reuse a key for a different payment.
  • Stable across retries — generate it before the first attempt and reuse the same value. A key generated per attempt provides no protection at all.
  • Scoped to your account — no need to worry about collisions with other partners.
If you take one thing from this page: generate the idempotency key when you decide to pay, store it alongside your own record, and send that same key on every retry. This is what makes a timeout safe.

Timeouts

If a payment request times out, don't assume it failed. Retry with the same idempotency key: you'll get the original payment if it landed, or a fresh one if it didn't. Either way you end up with exactly one payment.