# Pay-per-GB API

Pay-per-GB (PPG) pools, geo targeting values, and account balance.

## Pools

```http
GET /api/v1/pay-per-gb/pools
Authorization: Bearer lit_...
```

A complete list: every pool the account can select, always with `page.nextCursor: null`. Takes no `limit` or `cursor`.

```json
{
  "data": [
    {
      "name": "Residential",
      "proxiesType": "residential",
      "authKey": "residential-main",
      "pricePerGb": "4.50",
      "httpPort": 1337,
      "socks5Port": 5337,
      "hubs": [
        { "id": "us-11", "name": "New York, NY", "hostname": "hub-us-11-1.litport.net" },
        { "id": "eu-1", "name": "Amsterdam", "hostname": "hub-eu-1.litport.net" }
      ],
      "geoLevels": ["country", "region", "city"]
    }
  ],
  "page": { "nextCursor": null }
}
```

`authKey` is the only pool identifier this API exposes, and the value to put in the proxy username; no internal catalog ID is returned. `pricePerGb` is a base-10 decimal string already adjusted for the account's plan discount. It is the full effective rate, not a rounded display price, so a charge can be reproduced from billed bytes and this value. `httpPort` and `socks5Port` are the ingress ports for that pool's proxy type. `hubs` lists the hubs this pool can be routed through. `geoLevels` lists the levels this pool supports for [geo targeting](#geo), broadest first, or `[]` when the pool has no geo targeting.

Example identifiers and hubs in this documentation are illustrative.

## Geo

```http
GET /api/v1/pay-per-gb/geo
Authorization: Bearer lit_...
```

A paged list of geo targeting values: the country, region, or city values a set of pools actually supports, so a geo-blocked proxy request (a `country`/`region`/`city` parameter Litport does not recognize or does not have exit capacity for) can be corrected instead of guessed at again.

| Parameter | Type | Rules |
| --- | --- | --- |
| `pool` | string, comma-separated | Required, non-empty. Each value is a customer-selectable pay-per-GB pool's authKey. An unknown or non-selectable key returns INVALID_POOL. |
| `level` | string | Required. One of country, region, or city. |
| `country` | string | Lowercase geo slug. Required when level is region or city. |
| `region` | string | Lowercase geo slug. Required when level is city. |
| `q` | string | Optional search text, trimmed and capped at 80 characters. |
| `limit` | integer | Integer 1-100; default 50. |
| `cursor` | string | The previous page's page.nextCursor, sent back unchanged. |

```bash
curl --get 'https://litport.net/api/v1/pay-per-gb/geo' \
  -H "Authorization: Bearer $LITPORT_API_KEY" \
  --data-urlencode 'pool=residential-main' \
  --data-urlencode 'level=region' \
  --data-urlencode 'country=us' \
  --data-urlencode 'limit=50'
```

```json
{
  "data": [
    { "value": "california", "label": "California" },
    { "value": "new-york", "label": "New York" }
  ],
  "page": { "nextCursor": null }
}
```

`data` is the intersection of values every requested pool supports at the given `level`, so a returned value is guaranteed usable with all of them together. Each option's `value` is the exact lowercase slug to send as the corresponding `country`, `region`, or `city` proxy-username parameter; `label` is a display name, and country-level options may also carry `emoji`. `q` filters `data` by a case-insensitive, punctuation-insensitive substring match against `value` or `label`.

### Geo errors

Check a pool's `geoLevels` first to avoid the two `GEO_` errors.

| Status | Code | Meaning |
| --- | --- | --- |
| `400` | `INVALID_POOL` | `pool` is missing, unknown, or not selectable by this account. |
| `400` | `INVALID_LEVEL` | `level` is not `country`, `region`, or `city`. |
| `400` | `INVALID_PARENT` | `region` needs `country`; `city` needs `country` and `region`. |
| `400` | `GEO_NOT_ENABLED` | A requested pool has geo targeting switched off. |
| `400` | `GEO_LEVEL_UNSUPPORTED` | A requested pool does not support this `level`. |
| `400` | `INVALID_LIMIT` / `INVALID_CURSOR` | `limit` is not 1–100, or `cursor` is not one this list returned. |

## Balance

```http
GET /api/v1/pay-per-gb/balance
Authorization: Bearer lit_...
```

Use this to explain a proxy request that failed with [insufficient balance (code 13)](/docs/proxy-errors) without guessing at the cause.

```json
{
  "data": {
    "balance": {
      "unitsActive": "12.345678901234",
      "unitsReserved": "0.5",
      "unitsAvailable": "11.845678901234",
      "unitsExpireAt": "2026-12-01T00:00:00.000Z"
    },
    "plan": {
      "id": "pro-monthly",
      "expiresAt": "2026-10-01T00:00:00.000Z"
    }
  }
}
```

`unitsActive`, `unitsReserved`, and `unitsAvailable` are base-10 decimal strings so clients never lose precision to floating-point rounding. `unitsReserved` is non-negative and currently held by in-flight billing transactions, not yet final. `unitsAvailable` is `unitsActive` minus `unitsReserved` and is signed: it goes negative when reservations exceed the active balance, or when a finished run had to be charged beyond the available balance. Treat a request as at risk of an insufficient-balance error when `unitsAvailable` is at or near zero. `unitsExpireAt` is the timestamp of the soonest-expiring active balance, or `null` when none is scheduled to expire.

`plan.id` is the active plan identifier, or `null` when the account has no active plan. `plan.expiresAt` is that plan's expiration timestamp, or `null` when there is no active plan.

This endpoint reports balance only. It does not return email, name, or any other account profile field.

## Pay-per-GB tokens

A PPG token's `poolSelection` (see [Tokens](/docs/api/tokens)) names its fixed pool, or reports that the proxy username must select one. List an account's PPG tokens with `GET /tokens?type=ppg`. See [Pay-per-GB proxies](/docs/ppg-proxies) for the full username grammar, including how the pool, geo, and session parameters combine.
