Skip to content

Usage and data accounting

ProxyRequest accounts traffic in integer bytes. A reseller integration must distinguish the physical byte pool owned by a root order from the commercial limit assigned to each sub-user. They change during the same request, but they are not the same balance.

New to the integration? Read how purchases and traffic fit together first, then the expiration timeline. This page explains the lower-level accounting pipeline.

DATA INPUT HELPER

Convert allocation units to bytes

API UNIT: BYTES
Send this integer1073741824

Binary and decimal units are shown separately so the allocation is explicit.

Bandwidth is the standard metered billing model. Analytics also reports request counts, but request-count billing is a custom integration scope rather than a standard package mode. A single package does not combine bandwidth and request-count billing.

Concurrent connection limits can be configured for a user or assigned product context. RPS limits and a separate maximum sticky-session count are not standard SaaS controls.

Root order

A purchased order with no parent order. It owns the data ledgers for its user/package pair and can supply virtual child orders. Its owner can itself be a managed sub-user with an independent purchase.

Data ledger

A physical pool with total bytes, remaining bytes, and an optional expiry. Only root orders own ledgers.

Active ledger

The one ledger currently used by proxy authentication and accounting. A child order points to the same active ledger as its parent.

Child order

A sub-user’s independently assigned limit. Its data and data_spent fields enforce that customer limit without creating another physical pool.

TRAFFIC ACCOUNTING PIPELINEOne usage batch updates a shared pool and one consumer order
01 / PROXYCount transferred bytesHTTP, CONNECT, SOCKS5 TCP, and UDP traffic
02 / BATCHAggregate usageUser + order + active ledger + package
03 / ACCOUNTANTApply the same byte deltaLive balances in Access state, durable totals in Accounting records
04 / OUTCOMEEmit balance eventsContinue, switch ledger, or disable one child order
ROOT ORDER / SHARED POOL

Parent package

ACTIVELedger C4.0 GB left
NEXTLedger A2.5 GB left
DONELedger Bexpired / 0 B
CHILD ORDERS / COMMERCIAL LIMITS
Sub-user A3 GB assigned
remaining = 3 GB − data_spent
Sub-user B5 GB assigned
remaining = 5 GB − data_spent

Both consume the active parent ledger. Their assigned limits do not reserve bytes from that ledger.

The proxy counts transferred bytes while it handles HTTP requests, CONNECT tunnels, SOCKS5 TCP streams, and UDP traffic. For metered packages, it aggregates positive byte deltas with the consuming user, order, package, and active ledger identifiers before sending them to accountant.

Accountant applies each batch to two projections:

  1. Access state holds the working balance used by the live proxy path. Accountant decrements the active ledger and emits a balance-change or run-out signal.
  2. Accounting records hold the durable totals. Accountant groups the batch by ledger and order, then saves both values in one transaction:
data_ledgers.data_remaining = data_ledgers.data_remaining - bytes
orders.data_spent = orders.data_spent + bytes

The ledger update is grouped by ledger_id; the order update is grouped by the order that actually generated traffic. This is what makes root and sub-user accounting behave differently.

Traffic ownerPhysical balance that decreasesdata_spent that increasesPersonal remaining balance
Owner of a purchased root orderThat order’s active ledgerThat root orderNot separately calculated
Virtual child orderParent root order’s active ledgerThat child’s orderchild.data - child.data_spent
Unlimited packageNo byte-ledger decrementTraffic is still captured for analyticsNot byte-enforced

Assume the active parent ledger starts with 10 GB. Sub-user A is assigned 3 GB and consumes 1.2 GB; sub-user B is assigned 5 GB and consumes 0.8 GB.

Parent active ledger: 10.0 - 1.2 - 0.8 = 8.0 GB remaining
Sub-user A: 3.0 - 1.2 = 1.8 GB remaining
Sub-user B: 5.0 - 0.8 = 4.2 GB remaining
Parent order data_spent = 0 GB

The parent order’s own data_spent stays unchanged because the child orders were the consumers. The shared parent ledger still pays for both. Direct traffic made with the parent’s credentials would increment the parent order’s data_spent as well as decrement the same ledger.

POST /users/{id}/data/add and the matching subtract operation change the child order’s data allowance. They do not decrement the parent ledger, create a child ledger, or reserve bytes from the shared pool.

This makes overcommit possible: the sum of all child limits can be greater than the bytes currently available across parent ledgers. It is useful when not every customer consumes their full allowance, but your billing system should monitor both values:

  • child remaining tells you whether one customer may continue;
  • active ledger remaining tells you whether the whole package tree can continue.

Subtracting allocation cannot remove more than the child order’s current assigned data. It also synchronizes that child to the parent’s current active ledger when necessary.

A root order can keep more than one valid ledger. This lets separate purchases retain their own expiry and lets accountant move the whole order tree without rebuilding credentials.

  1. A paid, expiring, limited-data invoice creates a separate ledger so that purchase can expire independently.
  2. Unlimited packages and non-expiring purchases merge new data into a compatible ledger instead of creating another expiring pool.
  3. The ledger created or updated by the latest paid invoice becomes active immediately.
  4. The API synchronizes the root order and all child orders to that active ledger.
  5. When the active ledger reaches zero or expires, accountant marks it expired and selects the oldest-created remaining valid ledger.
  6. The replacement ledger ID is written to the entire root/child tree and projected into proxy caches.
  7. If no replacement exists, active_ledger becomes empty for the whole tree and metered proxy access cannot continue.

A ledger is eligible as a replacement only when it belongs to the root order, is not marked expired, has data_remaining > 0, and has no expiry or an expiry in the future. Created time and ledger ID provide deterministic FIFO ordering between eligible replacements.

This FIFO rule describes accountant replacement. Python-side recovery orders eligible ledgers by expiration, creation time and ID instead. REST ledger ordering is neither an active-ledger indicator nor a universal spending-priority guarantee. See ledger lifecycle for customer-facing consequences.

ConditionAccountant actionWho loses accessEvent meaning
Active ledger remains above zeroKeep the current ledgerNobodyuser.data.change reports its latest remaining bytes
Active ledger reaches zero or belowExpire it and transition the whole treeNobody if a replacement exists; otherwise root and childrenuser.data.run_out identifies the exhausted ledger
Finite child limit reaches zero or belowClear only that child order’s active ledger and invalidate its cacheThat sub-user onlyorder.data.run_out reports the child limit exhaustion
Ledger expires by timeRun the same whole-tree transitionDepends on replacement availabilityExpiry and byte run-out share the transition model

Usage is processed in batches, so a balance can cross zero rather than stop exactly at zero. Both webhook order.data and order.data_remaining can therefore be negative and represent overdraft from the final batch.

order.data

Remaining bytes in the ledger named by order.ledger_id after the usage batch. It describes the shared physical pool, even when a child user generated the traffic.

order.data_remaining

Remaining bytes in a finite child order. It is null for root and unlimited orders; null does not mean zero.

Webhooks are optimized for reacting to balance changes, transitions, and run-out. They may be coalesced, delayed, or retried. Use analytics and reconciliation for closed reporting windows and the webhook event reference for exact payload fields.