# /private-pool - Proxies.sx Private Pool API > Programmatic control of a **Private Pool**: a scoped, named slice of the proxy network with its own credential, its own country list, and optional **Reserved IPs** - specific exit devices held for you alone so nobody else routes through them. > **Hierarchy:** this file is the canonical Private Pool API contract. If any other file disagrees with it, this file wins. If this file disagrees with the live API, the live API wins. **Base URL:** `https://api.proxies.sx/v1` **Auth:** every endpoint below takes an account API key. Both forms work: ```bash -H "X-API-Key: psx_YOUR_KEY" # or -H "Authorization: ApiKey psx_YOUR_KEY" ``` A browser-session JWT (`Authorization: Bearer `) works on the same routes. Mint an API key at https://client.proxies.sx/api-keys or `POST /v1/api-keys`. Every route is owner-scoped: you only ever see and mutate your own pools. A pool id you do not own returns `404`, never another tenant's data. --- ## Is this the product you want? | | Pool Gateway | **Private Pool** | Dedicated Port | |---|---|---|---| | What you get | one credential over the whole network | a **named, country-scoped** credential + optional held IPs | one real modem on its own host:port | | Exit devices | shared | shared, or **Reserved (exclusive)** | exclusively yours | | Countries | any, per-request | the list you scope the pool to | one, fixed at purchase | | Price | $4.00/GB | $4.00/GB (same meter) | $4.00/GB | | Docs | [/pool/skill.md](https://agents.proxies.sx/pool/skill.md) | this file | [/marketplace/skill.md](https://agents.proxies.sx/marketplace/skill.md) | Private Pool is the right choice when you want **separate credentials per project/customer** with their own usage series, or when you need **the same exit IP held over days** (Reserved IPs). > **Before you scope a pool to a country, check that country has real depth.** > `curl -s https://api.proxies.sx/v1/gateway/pool/stock` returns live endpoint counts per country. A country with a handful of endpoints will be unreliable no matter what you configure. This is the single most common cause of "my private pool does not work". --- ## Funding models A pool is funded one of two ways. **Which one you have changes what the credential meters against**, so read this before integrating. | | `fundingSource: "balance"` | `fundingSource: "purchase"` | |---|---|---| | Created by | `POST /private-pool/build-from-balance` | `POST /private-pool/requests` then `POST /:id/activate` | | Budget | your account's global GB balance | GB from the specific purchase claimed by that pool | | `credit.allocatedGB` | a display-only earmark (`dedicatedGb`) | the pak's hard cap | | Runs out when | your **account** GB hits zero | the pool's own cap is reached | On a balance-funded pool, `credit.allocatedGB` is a guide, not a separate bucket - the real gate is `credit.accountAvailableGB`. Meter against that. --- ## Quickstart ```bash KEY="psx_YOUR_KEY" # 1. list your pools (each with a ready-to-use connection block) curl -s https://api.proxies.sx/v1/private-pool/pools -H "X-API-Key: $KEY" # 2. reveal the pool's secret (audit-logged) curl -s -X POST https://api.proxies.sx/v1/private-pool/POOL_ID/reveal -H "X-API-Key: $KEY" # 3. connect - substitute the revealed pak_ as the password curl -x "http://psx_ACCOUNT-best-us-sid-pool01-rot-sticky:pak_SECRET@gw.proxies.sx:7000" \ https://api.ipify.org ``` `GET /pools` already returns `connection.strings[]` with a ready username per country - build from those rather than assembling the DSL yourself. --- ## Endpoints ### Read #### `GET /private-pool/pools` Every non-deleted pool you own, each with its live connection block. **This is the endpoint to poll.** ```jsonc [{ "pool": { "id": "6a6708230f7bd445c7077a8c", "label": "eu-scrapers", // null until you set one "poolType": "mbl" | "peer" | "any", "deviceCount": 10, "countries": ["US","GB","FR"], "allowedCountries": ["US","GB","FR"], // effective scope; null = flexible "requestedCountries": [], // pending additions, if any "qualityTier": "standard" | "safe", // 'safe' = carrier modems only "failoverDefault": "samecountry", "termMonths": 1, "purchasedGB": 50, "dedicatedGb": 20, // display earmark on balance-funded pools "fundingSource": "balance" | "purchase", "status": "requested" | "active" | "suspended" | "expired" | "cancelled", "expiresAt": "2026-08-27T00:00:00.000Z", "createdAt": "2026-07-27T07:26:27.000Z" }, "connection": { // null while status is 'requested' "host": "gw.proxies.sx", "httpPort": 7000, "socksPort": 7001, "proxyUsername": "psx_...", // the account id to authenticate as "poolToken": "best" | "mbl" | "peer", "countries": ["US","GB","FR"], "failoverDefault": "samecountry", "usernameTemplate": "psx_...-best-{country}[-sid-{name}][-rot-...]", "strings": [ { "country": "US", "username": "psx_...-best-us-sid-pool01-rot-sticky", "http": "http://psx_...-best-us-sid-pool01-rot-sticky:pak_********@gw.proxies.sx:7000" } ], "credit": { "allocatedGB": 20, "usedGB": 1.597, "remainingGB": 18.403, "expiresAt": null, "enabled": true, "note": "Draws from your account GB", // balance-funded only "accountAvailableGB": 18.4 // balance-funded only - THE REAL GATE }, "stockUrl": "https://api.proxies.sx/v1/gateway/pool/stock", "notes": ["..."] } }] ``` The password in `strings[].http` is masked as `pak_********`. Call `reveal` to get the real secret. #### `GET /private-pool/mine` The single most recent pool, same shape wrapped as `{ pool, connection }`. Returns `{"pool": null}` when you have none. Prefer `/pools`. #### `GET /private-pool/:id/usage` · `GET /private-pool/mine/usage` Per-day MB series for the pool. Query: `?days=30` (1-365, default 30). ```jsonc { "series": [{ "date": "2026-07-27", "mb": 412.5 }], "totalMB": 1634.2 } ``` #### `GET /private-pool/:id/sessions` Live gateway sessions currently open on this pool's credential. ```jsonc { "count": 3 } ``` Returns `{"count": 0}` on any gateway hiccup - it is a live indicator, never a blocker. ### Credential #### `POST /private-pool/:id/reveal` · `POST /private-pool/mine/reveal` Returns the full secret. **Audit-logged every time.** ```jsonc { "key": "pak_a1b2c3..." } ``` #### `POST /private-pool/:id/regenerate` · `POST /private-pool/mine/regenerate` Issues a new secret and **immediately invalidates the old one**. The username does not change, so only the password in your config needs updating. In-flight connections are not torn down; new connections with the old secret get `407`. ```jsonc { "key": "pak_NEW..." } ``` ### Create #### `POST /private-pool/build-from-balance` Creates **and activates** a pool funded by your existing account GB. No purchase needed. Rate limit: **5/min**. ```bash curl -s -X POST https://api.proxies.sx/v1/private-pool/build-from-balance \ -H "X-API-Key: $KEY" -H "Content-Type: application/json" \ -d '{"deviceCount": 10, "countries": ["US","GB"], "dedicateGB": 20}' ``` | field | type | notes | |---|---|---| | `deviceCount` | int | how many devices to spread across | | `countries` | string[] | ISO-2 codes; check stock depth first | | `dedicateGB` | int ≥1 | display earmark, **not** a hard cap | Requires ≥1 GB actually available on the account, or `400`. Returns the pool object (`status: "active"`). > Gated by a server flag. If the feature is off you get `503 Balance-funded pools are not available yet.` - use the request+activate path instead. #### `POST /private-pool/requests` → `POST /private-pool/:id/activate` The purchase-funded path. `requests` files the configuration (status `requested`, opens a ticket); `activate` claims a paid GB purchase and mints the credential. ```bash curl -s -X POST https://api.proxies.sx/v1/private-pool/requests \ -H "X-API-Key: $KEY" -H "Content-Type: application/json" \ -d '{"poolType":"mbl","deviceCount":10,"countries":["US","GB"]}' ``` A given purchase can fund exactly one pool - a second `activate` against the same purchase returns `400 This purchase was already applied to a private pool`. ### Modify #### `POST /private-pool/:id/request-countries` Adds countries to the pool scope **instantly and free**. Widen-only: nothing is ever removed. ```bash -d '{"countries":["DE","NL"]}' ``` `400` if every country you list is already in scope. Returns the updated pool. #### `PATCH /private-pool/:id/label` ```bash -d '{"label":"eu-scrapers"}' ``` Sanitised to `[\w\s-]`, max 60 chars. #### `POST /private-pool/mine/custom-request` Files a support ticket for a custom build. `-d '{"text":"..."}'`, minimum 5 characters. --- ## Reserved IPs (leases) A lease **holds one specific exit device for you exclusively** - no other customer is routed through it for the lease duration. This is what you use when a workflow needs the same IP across hours or days. > Sticky sessions pin the **modem**, not the IP - a mobile carrier can still re-issue the exit IP under you. A Reserved IP on a residential peer is the stable option. #### `GET /private-pool/:id/leases` ```jsonc [{ "id": "...", "country": "US", "carrier": "T-Mobile", "ipType": "residential", "status": "active", "expiresAt": "...", "rotationMode": "manual", "username": "psx_...-best-us-pin-lease-" }] ``` Exit IPs are never returned by this API. You get country, carrier, ASN and type - never the address. #### `POST /private-pool/:id/leases` Acquires a lease. Body selects what to hold (e.g. `{"country":"us","ipType":"residential"}`). Fails if no matching device is free. #### `POST /private-pool/leases/:leaseId/rotate` Swaps the held device for another matching one. The lease id and its username stay the same, so **your connection string never changes**. #### `POST /private-pool/leases/:leaseId/extend` · `DELETE /private-pool/leases/:leaseId` Extend the hold, or release it back to the shared pool. #### `PATCH /private-pool/leases/:leaseId/rotation-mode` `{"mode":"manual"|"auto"}` plus an interval for `auto`. #### `POST /private-pool/leases/:leaseId/rotation-token` · `DELETE …` Mints (or revokes) a token for unauthenticated rotation - useful from a scraper that should not hold your API key: ```bash curl -s https://api.proxies.sx/v1/private-pool-rotate/ ``` This is the **only** unauthenticated endpoint in this file. Treat the token as a secret; anyone holding it can rotate that lease. ### Using a lease Pin with `-pin-lease-` in the username: ```bash curl -x "http://psx_ACCOUNT-best-us-pin-lease-LEASE_ID:pak_SECRET@gw.proxies.sx:7000" \ https://api.ipify.org ``` > Use `-pin-lease-`, never `-pin-device-`, for leases. The username parser **silently drops** pin types it does not recognise, so a typo does not error - it quietly routes you to a random shared exit. If a "reserved" IP keeps changing, check this token first. --- ## Routing (username DSL) The password is the pak secret; everything else is declared in the username. Full cheatsheet: [/pool/skill.md](https://agents.proxies.sx/pool/skill.md#routing-parameters-username-dsl---full-cheatsheet). ``` psx_--[-sid-][-rot-][-pin-lease-][-failover-] ``` | token | values | notes | |---|---|---| | `pool` | `mbl` \| `peer` \| `best` \| `any` | `best` = modem-preferred with peer failover | | `country` | ISO-2 or `any` | must be in the pool's scope | | `sid` | 1-64 chars | **required for stickiness to survive across connections** | | `rot` | `sticky` \| `auto5/10/20/60` \| `ondemand` \| `hard` | default `auto10` | | `failover` | `any` \| `samecountry` \| `samecarrier` \| `samenode` \| `strict` | default `samecountry` | **Two footguns that account for most "it does not work" reports:** 1. `-sid-` alone is **not** sticky. Stickiness needs `-rot-sticky` **and** a `-sid-`. Without a `-sid-` every connection gets a fresh throwaway session. 2. `-session-` is **not** a real token. It is silently ignored. The token is `-sid-`. --- ## Errors | status | meaning | |---|---| | `400` | bad input, or the action is not applicable (already-claimed purchase, countries already in scope, insufficient account GB) | | `401` | missing/invalid API key | | `404` | pool or lease not found **or not owned by you** | | `503` | feature flag off, or a dependency (balance check) is unavailable - fail-closed by design | Gateway-side errors (returned as `CODE: message (req: uuid)` on the proxy connection, not this REST API): | code | meaning | |---|---| | `E_AUTH_INVALID` | wrong secret, or the key is disabled | | `E_CAP_EXCEEDED` | the pool's GB cap (or the owner's account balance) is exhausted | | `E_NO_STOCK_COUNTRY` | no live endpoints for that country - check `/gateway/pool/stock` | | `E_RATE_LIMITED_AUTH` | too many failed auths; back off, do not retry in a loop | --- ## Operational notes - **Rate limits:** `build-from-balance` 5/min. Others follow the standard account limits. - **Metering:** every byte is metered against the pool's budget. There is no separate per-pool invoice - it draws on the same GB you bought. - **Reveal is audited:** each `reveal` writes an audit record. Cache the secret in your own secret store rather than calling it per request. - **Exit IPs are never exposed** by any endpoint here, by design - published exit IPs get burned by anti-bot IP-reputation systems, which would degrade the network for everyone. ## See also - [Pool Gateway](https://agents.proxies.sx/pool/skill.md) - the one-credential product and the full DSL - [Rotation Cookbook](https://agents.proxies.sx/pool/rotation-cookbook.md) - copy-paste rotation in curl/python/node - [Dedicated Port](https://agents.proxies.sx/marketplace/skill.md) - one modem, its own host:port - Live stock: `https://api.proxies.sx/v1/gateway/pool/stock`