The reseller ledger explained (allocate, reclaim, top-up, and what "unused" means)

2026-10-07 · Bazyl

After this you can read your own ledger top to bottom, prove that your pool balance is its sum, and know where to look for the one number it doesn't carry. Most partners open the ledger to find out how much their customers have used. It never says. It records moves, not usage.

Four kinds of row

GET /ledger is your pool's history, newest first. Every row is a signed delta in megabytes with its product and its kind, and there are four kinds:

  • SALE, positive: your wholesale purchase, written when the sale is recorded
  • ALLOCATE, negative: gigabytes moved from your pool to a customer
  • RECLAIM, positive: gigabytes moved back from a customer by balance/remove, suspend, kill or delete
  • ADJUST, either sign: a correction

The rows come back in entries[] with a nextCursor. Pass it back as ?cursor= until it comes back null, and you have the whole history. Filter with ?product=residential2 to see only the residential pool, since every row carries its product.

A concrete row: a customer created with initialGb: 20 writes one ALLOCATE of −20,000 MB. 1 GB is 1,000 MB in the ledger, as it is everywhere else on our side.

The pool is the sum of the ledger

Your pool balance equals the sum of every ledger row for that product. Not roughly: exactly, and you can check it in a loop. GET /me reports the balance under pools[], the ledger reports the deltas, and the two must agree.

# Sum every ledger row for the residential pool and compare it with GET /me.
API="https://api.basilproxies.com/api/reseller/v1"
AUTH="Authorization: Bearer $BASIL_PARTNER_KEY"

sum=0; cursor=""
while :; do
  page=$(curl -s "$API/ledger?product=residential2${cursor:+&cursor=$cursor}" -H "$AUTH")
  sum=$(( sum + $(echo "$page" | jq '[.entries[].deltaMb] | add // 0') ))
  cursor=$(echo "$page" | jq -r '.nextCursor // empty')
  [ -z "$cursor" ] && break
done

pool=$(curl -s "$API/me" -H "$AUTH" \
  | jq '.pools[] | select(.product == "residential2") | .balanceMb')
echo "ledger sum: $sum MB   pool: $pool MB"

Run it after the week in the first-100-GB post and both numbers read 72,000: a SALE of +100,000, two ALLOCATEs of −20,000 and −10,000, and a RECLAIM of +2,000. If the two ever disagree, that is a ticket, with both numbers in it.

One note on the loop. Every page counts against your rate limit, 120 requests a minute by default, and past it the API answers 429 RATE_LIMITED with a Retry-After header in seconds. Wait that long and run the same page again.

Sales live on their own endpoint

Money is not in the ledger. GET /sales is your purchase history, newest first, each row with its status, eurPerGb and bankReference, paged with the same cursor. The ledger's SALE row tells you gigabytes arrived; the sales row tells you what you paid per gigabyte and which transfer it was.

status is one of three. COMPLETED is the normal case: the gigabytes are in the pool and the SALE row exists. PENDING exists in the API for compatibility; on the residential pool a purchase either completes or is refused outright, so you won't meet one. FAILED never credited your pool, and if you see one, that's a ticket too.

So a reconciliation against your bank statement is GET /sales?status=COMPLETED, matched on bankReference, not the ledger. Keep the two jobs apart and both stay short.

The net-zero pair

The one pattern that looks like an error isn't. Usage is metered as it happens and the cap enforces with a short lag, so a busy customer can finish a little past what you gave them. When you next top them up, the excess is written off first and the full top-up is added after.

In the ledger that is three rows: an ADJUST credit for the overrun, written by the system, an ALLOCATE debit of the same size, and then the ALLOCATE for your top-up. The first two net to zero. Your pool is not charged for the overrun, and the customer gets every megabyte they paid for.

Example: a customer finished 1,200 MB over, and you add 10 GB. You see ADJUST +1,200, ALLOCATE −1,200, ALLOCATE −10,000. The net effect on your pool is −10,000 MB, exactly the top-up.

"Unused" is not in the ledger

The ledger can't tell you how much of an allocation is still sitting on a customer. Allocated minus burned is a usage question, and the ledger records moves, not usage. A customer you funded with 20 GB shows one row, −20,000 MB, whether they've burned none of it or all of it.

Two endpoints hold the usage side. GET /usage?days=30 is your whole book by day: used MB, remaining balance and customer count, built from daily snapshots. GET /customers/:id/usage?duration=30d is one customer's daily points; 24h, 7d and 90d are the other windows. There is no hourly series on this pool, so don't build a dashboard that expects one.

For one customer's remaining balance right now, read the customer itself. The default read is cached and at most about 45 seconds old; ?fresh=true reads the live balance and has its own budget of 6 a minute, so poll the cached one and keep the live one for a dispute.

"Unused" for the whole book is the latest day's remaining balance in /usage. For one customer it is what you allocated minus what their usage series adds up to. Both come from usage, neither from the ledger.

What the ledger does not do

It doesn't record usage, it doesn't record money, and it doesn't hold an hourly series. It records every movement of gigabytes between your pool and your customers, and it does that completely, which is why the sum check above works.

If you've read this far and still have no pool to sum, the partner page is where the first 100 GB starts.

Common questions

What does a negative row in the partner ledger mean?
An ALLOCATE, gigabytes moved from your pool to a customer. SALE and RECLAIM rows are positive. ADJUST is a correction and can go either way.
Does the ledger show how many gigabytes my customers have used?
No. The ledger records moves between your pool and your customers. Usage comes from GET /usage?days=30 for your whole book and GET /customers/:id/usage per customer, both as daily points.
Why does my ledger have two rows that cancel out?
That is the overrun write-off. When a customer's usage ran past their allocation, the next top-up writes the excess off as a SYSTEM ADJUST credit and a matching ALLOCATE debit, net zero to your pool, and then adds the full top-up.
Can a wholesale purchase sit pending?
Not on the residential pool. A purchase either completes and writes its SALE row or it is refused outright. The API keeps a PENDING status for compatibility, and on your account it is always empty. A FAILED row never credited your pool.