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.