# Litport full documentation

Generated from canonical documentation sources.

<!-- Source: /docs -->

# Litport documentation

Use Litport as a proxy provider or retrieve the public free-proxy list. These pages describe the deployed behavior, including exact credentials, limits, errors, and response shapes.

## Choose an integration

| Goal | Start here | Authentication |
| --- | --- | --- |
| Retrieve public free proxies | [Free proxy API](/docs/free-proxy-api) | None |
| Connect through a purchased proxy | [Tokens, proxy products, and hubs](/docs/proxy-concepts) | Proxy token username and password |
| Select PPG geography or a sticky session | [Pay-per-GB proxy parameters](/docs/ppg-proxies) | Parameters in the proxy username |
| Inventory existing tokens or read usage | [Account API](/docs/api) | Account API key in a Bearer header |

There is no account API endpoint to create, edit, disable, rotate, or delete proxy tokens.

## Free API quickstart

```bash
curl --get 'https://litport.net/api/free-proxy' \
  --data-urlencode 'country=us' \
  --data-urlencode 'protocol=https' \
  --data-urlencode 'limit=25'
```

The response is a bare JSON array. This endpoint is public and does not use an account API key.

## Account API quickstart

Create your account API key on [Settings](/users/settings), place it in an environment variable, then list existing tokens:

```bash
export LITPORT_API_KEY='lit_replace_with_your_key'
curl --fail-with-body 'https://litport.net/api/v1/tokens?limit=20' \
  -H "Authorization: Bearer $LITPORT_API_KEY"
```

The account API is read-only. Never put the key in a URL, proxy username, client-side application, log, or support message.

## Credentials and identifiers

| Term | Meaning |
| --- | --- |
| Proxy token username/password | Data-plane credentials sent to an HTTP or SOCKS5 proxy endpoint. |
| Proxy token ID | Stable non-secret identifier used by authenticated account API routes. |
| User API key (`lit_…`) | Account control-plane credential sent only as `Authorization: Bearer …`. |

An account API key is not a proxy password and cannot open a proxied connection. A proxy token ID is not a secret.

## Machine-readable resources

- [OpenAPI 3.1 contract](/docs/openapi.json)
- [Documentation index for coding agents](/llms.txt)
- [Combined Markdown documentation](/llms-full.txt)
- Every page is available as Markdown through its **Markdown** link.

The semantic HTML pages are canonical. The HTML, Markdown, navigation, and agent resources are generated from the same source files to reduce contract drift.

## Operational basics

- Use UTC ISO-8601 timestamps, for example `2026-08-25T12:00:00Z`.
- Treat byte counters as decimal strings; JavaScript clients should not coerce large values to `Number`.
- Read [proxy errors](/docs/proxy-errors) for data-plane failures and [account API errors](/docs/api/errors) for REST failures. They are different error systems.
- Use the `X-Request-ID` response header when reporting an account API failure.

---

<!-- Source: /docs/proxy-concepts -->

# Tokens, proxy products, and hubs

Litport separates the credential you buy from the network location you connect to and the upstream proxy that exits to the target.

## Request path

```text
client -> chosen Litport hub/ingress -> Litport routing -> selected upstream proxy -> target
```

A **token** is the proxy credential and commercial/accounting unit. A **hub** is a Litport ingress relay. The hub is not the exit country, region, city, ASN, or proxy pool.

## Unlimited and PPG tokens

| Product | Commercial model | Connection details |
| --- | --- | --- |
| Unlimited | Prepaid access until its expiration | Use its assigned hub and ingress protocol |
| Pay per GB (PPG) | Balance-backed access measured by upload plus download traffic | Select the pool per exported credential, or lock the token to one pool; choose an eligible hub and HTTP or SOCKS5 |

Unlimited tokens work only with their assigned hub and protocol. PPG tokens are reusable credentials: Configure & export chooses an eligible hub and protocol. Flexible tokens add the selected pool to the username; fixed tokens always use their saved pool. A PPG token's saved hub/protocol are exporter preferences, not authorization constraints.

## Proxy ingress

Use the endpoint shown for the token in the dashboard. Current default ports are:

| Hub family | HTTP | SOCKS5 |
| --- | ---: | ---: |
| Mobile and general | `1337` | `5337` |
| Datacenter and ISP | `31337` | `35337` |

A hub may supply an explicit custom port, which takes precedence. The dashboard endpoint is authoritative for a particular token.

HTTP ingress accepts forward-proxy requests and `CONNECT` tunnels. SOCKS5 ingress accepts supported SOCKS5 commands. Their authentication and error representations differ; see [Proxy error reference](/docs/proxy-errors).

## Credentials are purpose-specific

- Send the proxy username and password to the proxy endpoint.
- Use a token ID only to identify a token after authenticating to the account API.
- Send a user API key only to `https://litport.net/api/v1/...` in a Bearer header.

Do not use a user API key as a proxy password.

## Next steps

- [Understand connection, request, and speed controls](/docs/proxy-limits)
- [Build current PPG usernames](/docs/ppg-proxies)
- [Diagnose proxy failures](/docs/proxy-errors)
- [List token metadata](/docs/api/tokens)

---

<!-- Source: /docs/proxy-limits -->

# Connection, request, and speed limits

Litport enforces three distinct controls. The dashboard's legacy word **threads** means concurrent authenticated TCP proxy connections.

## Limit definitions

| Public term | Unit and scope | Enforcement |
| --- | --- | --- |
| Concurrent connections | Open authenticated TCP connections per token | A new connection may wait for the configured capacity window, then fails with proxy error `6`. |
| Request rate | Admission events per second per token | Fails with proxy error `10` when the per-token rate limit is reached. |
| Speed | Aggregate bidirectional bytes/second per token | Traffic is throttled or backpressured; exceeding speed does not itself produce a new proxy error. |

## What counts as a request

| Ingress mode | One request-rate event |
| --- | --- |
| Plain HTTP forward proxy, including HTTP keep-alive | Every HTTP request handled on the connection |
| HTTP `CONNECT` | The tunnel-opening request, once per tunnel |
| SOCKS5 TCP | The authenticated connection, once per connection |

## Speed units

Speed is reported as decimal megabits per second (`Mbps`), matching the dashboard and common network-service conventions. A 100 Mbps limit means 100,000,000 bits per second.

## Client guidance

- Reuse HTTP connections and tunnels when the target workflow allows it.
- Bound parallel work to the capacity configured for the token.
- On error `6`, close idle connections and retry only after capacity can free.
- On error `10`, use jittered exponential backoff and reduce the applicable request or connection admission rate.
- Treat slow writes or reads as normal speed backpressure; do not open extra connections merely to defeat it.

---

<!-- Source: /docs/proxy-errors -->

# Proxy error reference

Proxy errors are data-plane failures from HTTP or SOCKS5 ingress. They are separate from the JSON errors returned by the [account API](/docs/api/errors).

## HTTP proxy errors

For HTTP ingress, inspect these response headers before interpreting the HTTP status:

```text
X-Proxy-Error-Code: 10
X-Proxy-Error-Message: Too many requests
```

The numeric header is the primary discriminator. HTTP status alone is insufficient: several compatibility paths use `500`, while some network failures are inferred as `503`. Codes 11, 12, and 13 can carry a more specific wire message than the default below.

| Code | Symbol | Default or wire message | HTTP status(es) | Retry guidance | What to do |
| --- | --- | --- | --- | --- | --- |
| 0 | `ErrorNone` | Empty | No error response | Not applicable | Treat the request as successful. |
| 1 | `ErrorPanic` | panic | 500 | No tight-loop retry | Retry once with backoff, then contact support with request evidence. |
| 2 | `ErrorInternal` | Internal proxy error | 500 or 503 | Bounded backoff | Retry a limited number of times, then contact support. |
| 3 | `ErrorInterrupted` | Connection was interrupted | 500 | Only when the application operation is safe | Check client and target timeouts before retrying. |
| 4 | `ErrorNoToken` | Token not found | 407 | After correcting credentials | Check the proxy username, password, host, and port. |
| 5 | `ErrorNoAccess` | Access denied | 500 for compatibility | No automatic retry | Check token status, account access, and destination policy. |
| 6 | `ErrorLimited` | You have reached the limit of concurrent connections | 403 | After capacity is available | Close idle connections or reduce concurrency. |
| 7 | `ErrorNoProxy` | Internal proxy error | 500 | Bounded backoff | Retry briefly; contact support if no upstream becomes available. |
| 8 | `ErrorHubMismatch` | Proxy host mismatch, check token settings | 500 | After correcting the hub | Use the hub shown for the token in the dashboard. |
| 9 | `ErrorHubProxyTypeMismatch` | Proxy type mismatch, check token settings | 500 | After correcting the ingress | Use the token's current HTTP or SOCKS5 endpoint. |
| 10 | `ErrorRateLimited` | Too many requests | 403 | Backoff required | Reduce new-request or new-connection rate for the ingress mode. |
| 11 | `ErrorPpgGetProxy` | PPG proxy error May be replaced by a specific wire message. | 500 | Depends on the custom message | Correct pool, geography, or session parameters; otherwise retry with backoff. |
| 12 | `ErrorPpgStats` | PPG stats error The wire message may be custom. | 500 | Bounded backoff | Retry briefly, then contact support if the error continues. |
| 13 | `ErrorPpgNotEnoughUnits` | Insufficient balance The wire message may be custom. | 500 | Not a network retry | Check or add account balance in the dashboard. |
| 14 | `ErrorRemoteProxy` | Remote proxy connection error | 500 or 503 | Bounded backoff | Retry may select a different remote proxy. |
| 15 | `ErrorUpstreamConnection` | Upstream server connection error | 500 or 503 | Depends on destination safety | Check the destination and retry policy before retrying. |
| 16 | `ErrorPpgUpstreamResponse` | Internal proxy error | 500 | Bounded backoff | Retry briefly; if it persists, contact support with the hub, token ID, destination, timestamp, and code 16—never the proxy password. |

## PPG upstream failures

Code `16` means a pay-per-GB upstream could not serve the request. It does not mean the destination returned Litport's `500`. Litport deliberately replaces the upstream provider's status, headers, and body with the generic error.

HTTP clients should branch on `X-Proxy-Error-Code: 16`, not the HTTP status alone. SOCKS5 clients receive general failure without the numeric header; only reproduce a safe, idempotent request through the token's HTTP ingress to inspect the Litport code.

Retry with bounded backoff. If the error persists, contact support with the hub, token ID, destination, timestamp, and code `16`—never the proxy password. Traffic exchanged after admission, including the generic error response, remains subject to the existing usage policy.

## SOCKS5 replies

SOCKS5 does not expose `X-Proxy-Error-Code` or `X-Proxy-Error-Message`. Multiple internal categories intentionally collapse into one reply.

| Reply | Meaning | Possible categories |
| --- | --- | --- |
| `0x01` | General failure | Internal, upstream selection, PPG upstream, remote connection, or upstream I/O failure. Commonly codes 2, 7, 14, 15, or 16. |
| `0x02` | Connection not allowed | Authentication, token, hub/protocol, PPG, concurrent-connection, and rate-limit failures share this reply. |
| `0x07` | Command not supported | The requested SOCKS5 command is not implemented; this is not an X-Proxy-Error-Code. |

To disambiguate, reproduce the same **safe, idempotent** request and credentials against the corresponding HTTP ingress, then inspect its headers. If reproduction would repeat a side effect or is otherwise unsafe, do not reproduce it.

## Safe retry policy

1. Correct credentials, hub, protocol, geography, balance, or client concurrency before retrying deterministic failures.
2. Retry transient internal, selection, and upstream failures with bounded exponential backoff and jitter.
3. Retry an interrupted or upstream application operation only when repeating that operation is safe.
4. Never run a tight retry loop. Preserve the first and last evidence from a bounded sequence.

## Support evidence

Include the Litport hub, HTTP or SOCKS5 ingress, UTC timestamp, token ID, destination host and port, and observed error code/message or SOCKS5 reply. Include the request method when relevant.

---

<!-- Source: /docs/free-proxy-api -->

# Free proxy API

`GET /api/free-proxy` returns Litport's current public free-proxy observations. It requires no authentication.

## Request

```bash
curl --get 'https://litport.net/api/free-proxy' \
  --data-urlencode 'country=de' \
  --data-urlencode 'protocol=http' \
  --data-urlencode 'uptimeRating=90' \
  --data-urlencode 'sortBy=responseTimeMs_asc' \
  --data-urlencode 'limit=50'
```

| Parameter | Type | Default | Current behavior |
| --- | --- | --- | --- |
| `country` | string | all | Case-insensitive ISO-2 code. The value is uppercased; no aliases are applied. |
| `limit` | integer | 50 | Accepted range 10–1000. Invalid or out-of-range values are ignored. |
| `page` | integer | 1 | Accepted range 1–1000. Invalid or out-of-range values are ignored. |
| `sortBy` | string | pingAt_desc | Field: pingAt, responseTimeMs, or createdAt. Direction: asc or desc. Invalid values are ignored. |
| `anonymityLevel` | string | all | Exact filter. Unknown values usually return an empty array. |
| `protocol` | string | all | Exact filter such as http, https, or socks5. Unknown values usually return an empty array. |
| `uptimeRating` | integer | none | Minimum 24-hour uptime percentage, 1–100. Invalid values are ignored. |
| `responseTimeMs` | integer | none | Maximum HTTP response time in milliseconds, 1–10000. Invalid values are ignored. |
| `format` | string | json | json, csv, txt, or txt-proto. Unknown values fall back to JSON. |
| `download` | flag | off | When present for a supported format, returns an attachment. |

`country` uses a case-insensitive ISO-2 code and is uppercased for lookup. It has no alias table: use `country=gb`; `country=uk` may return an empty array. PPG proxy usernames have different compatibility behavior.

## Response behavior

The default JSON response is a bare array with no `data` envelope, total, or `hasMore` value. Pagination is blind: page offset is `(page - 1) * limit`, and an empty array means no rows matched that page.

Responses send `Cache-Control: no-cache, no-store, must-revalidate`. Do not rely on an intermediary cache. Invalid numeric parameters and invalid `sortBy` values are silently ignored in this version, leaving their defaults. Unknown exact filter values commonly produce an empty array rather than a `400` response.

```json
[
  {
    "protocol": "http",
    "host": "192.0.2.10",
    "port": 8080,
    "externalIp": "192.0.2.10",
    "geoCountry": "US",
    "responseTimeMs": 420,
    "uptimeRating": 97,
    "pingAt": "2026-08-25T12:00:00.000Z"
  }
]
```

Example addresses in this documentation are reserved for documentation and are not live proxies.

## JSON fields

| Field | Type | Meaning |
| --- | --- | --- |
| `protocol` | string | Proxy protocol. |
| `host` | string | Proxy hostname or IP address. |
| `port` | integer | Proxy port. |
| `externalIp` | string | Observed exit IP. |
| `geoCountry` | string | ISO-2 exit country. |
| `geoCountryFlagEmoji` | string | Country flag for display. |
| `geoRegion` | string | Observed exit region. |
| `geoCity` | string | Observed exit city. |
| `geoTimezone` | string | Observed exit timezone. |
| `asn` | integer | Observed autonomous system number. |
| `asnOrgName` | string | Observed network organization. |
| `anonymityLevel` | string | Measured anonymity classification. |
| `anonymityLevelRating` | integer | Normalized anonymity score. |
| `responseTimeMs` | integer | Measured HTTP response time in milliseconds. |
| `responseTimeRating` | integer | Normalized response-time score. |
| `uptimeRating` | integer | Rounded 24-hour uptime percentage. |
| `createdAt` | date-time | When Litport first observed this proxy. |
| `pingAt` | date-time | When Litport last checked this proxy. |

`createdAt` records when Litport first observed the proxy. `pingAt` records the last check and is the relevant freshness signal.

## CSV and text formats

```bash
# CSV
curl 'https://litport.net/api/free-proxy?format=csv&limit=25'
```

```bash
# host:port, one proxy per line
curl 'https://litport.net/api/free-proxy?format=txt&limit=25'
```

```bash
# protocol://host:port, one proxy per line
curl 'https://litport.net/api/free-proxy?format=txt-proto&limit=25'
```

Use `download=1` with a supported format to request an attachment. CSV is returned as `text/csv`; text variants use `text/plain`; JSON uses `application/json`.

## JavaScript example

```js
const url = new URL('https://litport.net/api/free-proxy')
url.searchParams.set('country', 'us')
url.searchParams.set('limit', '25')

const response = await fetch(url)
if (!response.ok) throw new Error(`HTTP ${response.status}`)
const proxies = await response.json()
```

## Python example

```python
import json
import urllib.parse
import urllib.request

query = urllib.parse.urlencode({"country": "us", "limit": 25})
with urllib.request.urlopen(f"https://litport.net/api/free-proxy?{query}") as response:
    proxies = json.load(response)
```

---

<!-- Source: /docs/ppg-proxies -->

# Pay-per-GB proxy parameters

PPG proxy selection parameters are suffixes appended to the proxy username. Use Configure & export in the dashboard to choose the pool, hub, protocol, location, and session.

## Connection scope

One PPG credential works with each eligible hub and with HTTP or SOCKS5. The selected pool determines routing and price. A flexible token selects its pool with `_pool-...`. A fixed token uses its saved pool and does not need that suffix; a different pool suffix is rejected.

## Username grammar

Append zero or more underscore-delimited segments to the base proxy username:

```text
<base-username>_pool-residential-kilo_country-us_region-california_city-los-angeles_sid-crawl-42_sttl-600
```

| Parameter | Syntax | Rules |
| --- | --- | --- |
| `pool` | `_pool-<auth-key>` | Required in exported credentials. Lowercase pool key from the dashboard; maximum 80 characters. |
| `country` | `_country-<slug>` | Lowercase geo slug, maximum 80 characters. uk normalizes to gb. |
| `region` | `_region-<slug>` | Requires country when used as a canonical geo path. |
| `state` | `_state-<slug>` | Compatibility alias for region. Do not send state and region together. |
| `city` | `_city-<slug>` | Requires both country and region. |
| `sid` | `_sid-<session-id>` | Letters, numbers, and hyphens after sanitization; 3–15 characters. |
| `sttl` | `_sttl-<seconds>` | 1–86400 is honored. 0 or 86401–999999 currently falls back to 600; seven or more digits are invalid. |

Use lowercase dashboard slugs. City targeting requires both `country` and `region`. `state` is a compatibility alias for `region`; do not provide both. The lowercase country alias `uk` normalizes to `gb` in this username grammar, unlike the free-proxy API.

## Geography examples

```text
# country
<base-username>_pool-residential-kilo_country-us

# region inside a country
<base-username>_pool-residential-kilo_country-us_region-california

# city inside a region
<base-username>_pool-residential-kilo_country-us_region-california_city-los-angeles
```

Use the geography options shown for the selected pool in the dashboard. A syntactically valid slug can still be unavailable from that pool.

Invalid or ambiguous examples:

```text
<base-username>_city-los-angeles
<base-username>_region-california_city-los-angeles
<base-username>_country-us_region-california_state-california
```

## Sticky sessions

`sid` requests session affinity. After sanitization, the session ID contains letters, numbers, and hyphens and must be 3–15 characters.

```text
<base-username>_pool-residential-kilo_country-de_sid-cart-17_sttl-900
```

`sttl` controls the requested sticky lifetime in seconds. Values 1–86400 are honored. Current compatibility behavior maps `0` and values 86401–999999 to 600 seconds; seven or more digits are invalid. Prefer an explicit valid value and do not rely on fallback behavior.

## Errors and retries

PPG selection failures are normally HTTP proxy error code `11` with a specific message. A traffic-accounting failure is code `12`. Insufficient balance is code `13` and requires checking or adding balance rather than a network retry.

Read [Proxy error reference](/docs/proxy-errors) for the observable HTTP headers and SOCKS5 limitations. For PPG upstream failures, see [code 16 guidance](/docs/proxy-errors#ppg-upstream-failures).

---

<!-- Source: /docs/api -->

# Account API

The versioned account API provides read-only access to the signed-in customer's existing proxy-token metadata and usage.

## Available operations

Base URL:

```text
https://litport.net/api/v1
```

The initial version supports only:

- `GET /api/v1/tokens`
- `GET /api/v1/tokens/{tokenId}/usage`

There is no API endpoint to create, edit, disable, rotate, or delete proxy tokens.

## Authentication

Create an account API key on [Settings](/users/settings). Its full value remains available there after creation or rotation.

```bash
export LITPORT_API_KEY='lit_replace_with_your_key'
curl 'https://litport.net/api/v1/tokens' \
  -H "Authorization: Bearer $LITPORT_API_KEY"
```

External clients use the exact Bearer scheme. Do not send a key in the query string, JSON body, cookie, or proxy username. Store it as a secret, restrict access to server-side workloads, and rotate it if it may have leaked.

The **Run** controls on these documentation pages use your signed-in Litport account. If you are signed out, the control asks you to log in first.

## Credential boundaries

| Credential | Purpose |
| --- | --- |
| Proxy token username/password | Opens proxied data-plane connections. |
| Proxy token ID | Selects a proxy token in an authenticated account operation; not a secret. |
| User API key (`lit_…`) | Authenticates account control-plane requests. |

## Conventions

- Responses use JSON except for empty HTTP bodies.
- Time ranges are half-open: `from` is included and `to` is excluded.
- Timestamps must be canonical UTC ISO-8601 values ending in `Z`.
- Potentially large byte counters are decimal strings.
- Cursor values are opaque. Store and send the returned cursor unchanged; do not parse it.
- The default account API limit is 120 requests per 60-second window for each API key. Read `RateLimit-Limit`, `RateLimit-Remaining`, and `RateLimit-Reset`; a rejected request also includes `Retry-After`.
- `X-Request-ID` identifies the request for debugging.
- Authenticated responses use `Cache-Control: private, no-store` and `Vary: Authorization`.

## Machine contract

Use the [OpenAPI 3.1 document](/docs/openapi.json) for operation IDs, parameters, and schemas. Handle the [common error envelope](/docs/api/errors) before using individual endpoint data.

---

<!-- Source: /docs/api/errors -->

# Account API errors

Account API errors are REST JSON responses. They are not related to numeric [proxy error codes](/docs/proxy-errors).

## Error envelope

```json
{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Authentication failed.",
    "requestId": "req_example",
    "docsUrl": "https://litport.net/docs/api/errors"
  }
}
```

The same request ID appears in the `X-Request-ID` header. Log it with the HTTP status and error code. Never log the `Authorization` header.

## Status reference

| Status | Codes | Client action |
| ---: | --- | --- |
| `400` | `INVALID_LIMIT`, `INVALID_TYPE`, `INVALID_STATUS`, `INVALID_CURSOR`, `INVALID_TIME_RANGE`, `INVALID_GROUP_BY`, `INVALID_INCLUDE` | Correct the request. Do not retry it unchanged. |
| `401` | `UNAUTHORIZED` | Supply a current account API key using the Bearer scheme. The response does not reveal whether a key or user exists. |
| `404` | `TOKEN_NOT_FOUND` | Check the token ID and ownership. The response does not disclose another user's token. |
| `404` | `NOT_FOUND` | Correct the API route. |
| `405` | `METHOD_NOT_ALLOWED` | Use `GET`. The initial account API has no write or token-creation methods. |
| `429` | `RATE_LIMITED` | Respect `Retry-After` when present and use jittered backoff. |
| `503` | `SERVICE_UNAVAILABLE`, `TOKEN_DATA_INVALID` | Respect `Retry-After`, retry with bounded backoff, then contact support with the request ID. |

## Retry algorithm

Retry only `429` and transient `503` responses automatically. Honor `Retry-After`, add jitter, cap attempts, and make the total retry budget visible to the caller. All current operations are `GET`, but callers should still avoid unbounded retries.

## Error code details

### Invalid limit

`INVALID_LIMIT` means `limit` is not an integer from 1 through 100.

### Invalid type

`INVALID_TYPE` means `type` is not `unlimited` or `ppg`.

### Invalid status

`INVALID_STATUS` means `status` is not `active`, `expired`, or `disabled`.

### Invalid cursor

`INVALID_CURSOR` means the opaque cursor is malformed, edited, or cannot be authenticated. Restart pagination without a cursor.

### Invalid time range

`INVALID_TIME_RANGE` means a timestamp is not canonical UTC, `from` is not before `to`, or the requested span is too large.

### Invalid group by

`INVALID_GROUP_BY` means `groupBy` is not `hour`, `day`, or `month`.

### Invalid include

`INVALID_INCLUDE` means the requested expansion is incompatible with the token or grouping; charge components require a PPG token and hourly buckets.

### Unauthorized

`UNAUTHORIZED` covers missing, malformed, unknown, rotated, and ownerless keys without revealing which condition occurred.

### Token not found

`TOKEN_NOT_FOUND` covers both missing and unowned token IDs.

### Not found

`NOT_FOUND` means the requested `/api/v1` route does not exist.

### Method not allowed

`METHOD_NOT_ALLOWED` means a known read-only endpoint received a non-GET method. The response includes `Allow: GET`; there is no proxy-token creation method in this release.

### Rate limited

`RATE_LIMITED` means the API key exhausted its current account API window. Respect `Retry-After`.

### Token data invalid

`TOKEN_DATA_INVALID` means token metadata is temporarily unavailable. Retry later, then report the request ID.

### Service unavailable

`SERVICE_UNAVAILABLE` means an account API dependency is temporarily unavailable. Respect `Retry-After`.

## Authentication failures

The API deliberately returns the same `401` envelope for malformed, unknown, rotated, and ownerless keys. Create or rotate an account API key in [Settings](/users/settings). A rotation immediately invalidates the previous active key.

---

<!-- Source: /docs/api/tokens -->

# List existing tokens

List customer-visible metadata for the authenticated account's existing proxy tokens.

```http
GET /api/v1/tokens
Authorization: Bearer lit_...
```

This operation returns metadata and proxy credentials. It does not create or modify tokens.

## Query parameters

| Parameter | Rules |
| --- | --- |
| `limit` | Integer 1–100; default 50. |
| `cursor` | Opaque cursor returned by the preceding page. |
| `type` | `unlimited` or `ppg`. |
| `status` | `active`, `expired`, or `disabled`. |

Results are ordered newest first. Keep requesting the returned `page.nextCursor` until it is `null`. Treat the cursor as opaque and use it only with the same query parameters.

## Example

```bash
curl --get 'https://litport.net/api/v1/tokens' \
  -H "Authorization: Bearer $LITPORT_API_KEY" \
  --data-urlencode 'type=ppg' \
  --data-urlencode 'status=active' \
  --data-urlencode 'limit=20'
```

```json
{
  "data": [
    {
      "id": 12345,
      "type": "ppg",
      "status": "active",
      "label": "Production crawler",
      "createdAt": "2026-08-01T10:15:00.000Z",
      "expiresAt": null,
      "ingress": {
        "hub": "example-hub",
        "protocol": "http",
        "mode": "request-selected",
        "supportedProtocols": ["http", "socks5"]
      },
      "poolSelection": {
        "mode": "fixed",
        "pool": { "authKey": "residential-main", "name": "Residential Main" }
      },
      "username": "proxy-username",
      "password": "proxy-password"
    }
  ],
  "page": { "nextCursor": null }
}
```

Example identifiers and hubs in this documentation are illustrative.

## Field semantics

`type` is `ppg` or `unlimited` according to the token product.

`status` is `disabled` when explicitly disabled, otherwise `expired` when its expiration has passed, otherwise `active`.

For unlimited tokens, `ingress.mode` is `assigned`; `hub` and `protocol` are authorization constraints. PPG tokens use `ingress.mode: request-selected`; `hub` and `protocol` are dashboard/export preferences and `supportedProtocols` lists the choices. In both cases, ingress is not exit geography.

For PPG tokens, `poolSelection.mode` is `auth-parameter` when the proxy username must select a pool. It is `fixed` when the token is locked to one pool; the public `pool` descriptor names that pool without exposing an internal identifier. If a fixed pool cannot be described safely, `pool` is `null`. Use the dashboard exporter to build the correct credential.

`username` and `password` are the proxy credentials for the token. Use them with the endpoint shown in the dashboard.

---

<!-- Source: /docs/api/usage -->

# Read token usage

Read customer-visible traffic for one owned token.

```http
GET /api/v1/tokens/{tokenId}/usage
Authorization: Bearer lit_...
```

## Query parameters

| Parameter | Rules |
| --- | --- |
| `from` | Inclusive canonical UTC ISO-8601 timestamp. |
| `to` | Exclusive canonical UTC ISO-8601 timestamp; must be after `from`. |
| `groupBy` | `hour`, `day`, or `month`; default `day`. |
| `include` | Comma-separated `domains` and/or `charges`. `charges` requires `groupBy=hour`. |

When the range is omitted, the API uses the 30 complete UTC days before the current UTC day. Hour ranges are limited to 31 days, day ranges to 366 days, and month ranges to 36 months.

## Example

```bash
curl --get 'https://litport.net/api/v1/tokens/12345/usage' \
  -H "Authorization: Bearer $LITPORT_API_KEY" \
  --data-urlencode 'from=2026-08-01T00:00:00Z' \
  --data-urlencode 'to=2026-08-03T00:00:00Z' \
  --data-urlencode 'groupBy=day'
```

```json
{
  "data": {
    "tokenId": 12345,
    "range": {
      "from": "2026-08-01T00:00:00.000Z",
      "to": "2026-08-03T00:00:00.000Z",
      "groupBy": "day",
      "timezone": "UTC"
    },
    "trafficBasis": "customer_chargeable",
    "summary": {
      "requests": 42,
      "uploadedBytes": "12000",
      "downloadedBytes": "88000",
      "totalBytes": "100000"
    },
    "buckets": [
      {
        "start": "2026-08-01T00:00:00.000Z",
        "end": "2026-08-02T00:00:00.000Z",
        "requests": 42,
        "uploadedBytes": "12000",
        "downloadedBytes": "88000",
        "totalBytes": "100000"
      }
    ]
  }
}
```

## Usage semantics

The interval is half-open: `[from, to)`. Buckets are ordered by start time and are sparse; a missing bucket means no stored aggregate row, not an API error.

`trafficBasis` is `customer_chargeable`, so the totals match the traffic shown to the customer. Large byte totals are decimal strings so clients can preserve exact values. `requests` is a JSON integer.

The token must belong to the authenticated user. An unknown or unowned token returns the same `404 TOKEN_NOT_FOUND` response.

## Charge breakdown

For a PPG token, request `include=charges&groupBy=hour` to add the persisted charge to each hourly bucket. The expansion is rejected for unlimited tokens. A versioned charge contains the pool's public key and name, raw and charged upload/download bytes, contributing hubs and protocols, snapshotted base/effective rates and discount, and the exact amount deducted. A row without snapshots is labeled `Legacy PPG usage`; the API does not guess its pool or rate from current token settings.

Charge components are the billing record. Upload and download are both charged. A `reserved` charge can grow while late traffic for the hour arrives; a `finished` charge is final.

## Domain breakdown

Request `include=domains` to add `domainBreakdown` to `data`:

```json
{
  "domainBreakdown": {
    "limit": 150,
    "totalDestinationCount": 151,
    "displayedCount": 150,
    "truncated": true,
    "range": {
      "from": "2026-08-01T00:00:00.000Z",
      "to": "2026-08-03T00:00:00.000Z",
      "timezone": "UTC",
      "periodClamped": false,
      "endClamped": false
    },
    "items": [{
      "protocol": "tcp",
      "destination": "example.com",
      "port": 443,
      "requests": 42,
      "uploadedBytes": "12000",
      "downloadedBytes": "88000",
      "totalBytes": "100000",
      "lastSeenAt": "2026-08-01T12:00:00.000Z",
      "firstSeenAt": "2026-08-01T00:00:00.000Z"
    }],
    "other": {
      "protocol": null,
      "destination": "Other",
      "port": null,
      "requests": 1,
      "uploadedBytes": "1",
      "downloadedBytes": "2",
      "totalBytes": "3",
      "lastSeenAt": null,
      "firstSeenAt": null
    }
  }
}
```

The response includes at most the 150 destinations with the highest total traffic. Every remaining destination and telemetry overflow is combined into `other`; `truncated` reports when that grouping occurred. TCP `requests` are successful request counts and UDP `requests` are inbound datagrams. Domain data is stored as whole-hour UTC buckets and is limited to the most recent 31 days of the requested interval. The returned UTC `range` is the effective bucket interval: a past partial start is rounded down to its hour and a past partial end is rounded up to its next hour. An end clamped to the current time remains at that exact time, so the response does not claim coverage past the current time. The range also reports `periodClamped` or `endClamped` when adjusted.
