# List credit blocks
Source: https://developers.soax.com/api/account/credits
api/openapi.json GET /v1/account/credits
List your active credit blocks with their initial and remaining balances and expiry dates. Credits are consumed by proxy traffic according to the tier of the country the traffic routes through.
Requires the `account:read` scope.
# Get account
Source: https://developers.soax.com/api/account/get
api/openapi.json GET /v1/account
Retrieve your organization's account information: name, status (`active` or `suspended`), current plan identifier, and account creation date.
Requires the `account:read` scope.
# Get subscription
Source: https://developers.soax.com/api/account/subscription
api/openapi.json GET /v1/account/subscription
Retrieve your active subscription: plan, status, and current billing period. Returns 404 if the account has no active subscription.
Requires the `account:read` scope.
# Credits burned
Source: https://developers.soax.com/api/analytics/credits-burned
api/openapi.json POST /v1/proxy/analytics/credits-burned
Retrieve credits burned per day, broken down by tier. Each item holds one date and a `data` array whose `key` is the tier number (`"1"`, `"2"`, `"3"`) and whose `value` is the credits burned on that tier. The date range may span at most 365 days.
If the API key is restricted to specific packages, results (and any `package_ids` filter) are limited to those packages.
Requires the `proxy:analytics:read` scope.
# Usage summary
Source: https://developers.soax.com/api/analytics/summary
api/openapi.json POST /v1/proxy/analytics/summary
Retrieve aggregate credits burned and traffic (GB) for the selected date range, compared against the previous period of the same length (`previous_period.to_date` is the day before `from_date`). If the API key is restricted to specific packages, only those packages are included. The date range may span at most 365 days.
Requires the `proxy:analytics:read` scope.
# Traffic breakdown
Source: https://developers.soax.com/api/analytics/traffic
api/openapi.json POST /v1/proxy/analytics/traffic
Retrieve traffic (in GB) grouped by a dimension (`package`, `proxy_type`, `tier`, `country`, `domain`, or `ingress_ip`), as a series of interval buckets. When `group_by=package`, keys are package names. The date range may span at most 365 days. If `interval` is omitted it is chosen automatically from the range (< 24 h → `hour`, < 60 days → `day`, otherwise `month`). Explicit intervals are validated: `hour` needs a range of at most 24 hours, `day` between 24 hours and 180 days, `month` at least 60 days.
If the API key is restricted to specific packages, results (and any `package_ids` filter) are limited to those packages.
Requires the `proxy:analytics:read` scope.
# API authentication
Source: https://developers.soax.com/api/authentication
How to authenticate with the SOAX API using API keys: the Authorization header, scopes, package restrictions, and key lifecycle.
The SOAX API authenticates every request with an **API key** sent as a bearer token:
```bash theme={null}
curl https://api.platform.soax.com/v1/account \
-H "Authorization: Bearer s_live_YOUR_API_KEY"
```
API key secrets start with `s_live_`. A missing, malformed, unknown, or revoked key returns:
```json theme={null}
// 401 Unauthorized
{ "detail": "Invalid API key" }
```
**API key ≠ package key.** The API key (`s_live_…`) authenticates calls to `api.platform.soax.com`. The package key (a 10-character password, one per package) authenticates proxy traffic through `proxy.soax.com:1337` — that's the `password` field in [connection strings](/api/packages/connection-string) and the value rotated by [rotate package password](/api/packages/rotate-password). They are managed separately and are not interchangeable.
## Getting a key
Create keys in the dashboard under [**Settings → API keys**](https://platform.soax.com/settings/api-keys): click **Generate new API key**, choose a name and scopes, and optionally restrict the key to specific packages. The secret is displayed **once**, at creation — store it securely; it cannot be retrieved later. (Only a hash is stored server-side.)
You can also create keys programmatically with [`POST /v1/api-keys/`](/api/keys/create) — see [Key lifecycle](#key-lifecycle) below.
## Scopes
Each key carries a set of scopes, and each endpoint requires one. Calling an endpoint without the required scope returns:
```json theme={null}
// 403 Forbidden
{ "detail": "Missing required scope: proxy:packages:read" }
```
| Scope | Grants access to |
| ------------------------- | ----------------------------------------------------------------------------------------------------------- |
| `proxy:packages:read` | List/inspect packages, connection strings, and all location endpoints |
| `proxy:packages:write` | [Rotate package passwords](/api/packages/rotate-password) |
| `proxy:analytics:read` | All [analytics endpoints](/api/analytics/summary) |
| `account:read` | [Account](/api/account/get), [credits](/api/account/credits), and [subscription](/api/account/subscription) |
| `api-keys:read` | [List API keys](/api/keys/list) |
| `api-keys:write` | [Create](/api/keys/create) and [revoke](/api/keys/revoke) API keys |
| `team:read`, `team:write` | Reserved — no v1 endpoints use these yet |
[`GET /v1/api-keys/me`](/api/keys/me) is the one exception: any active key can call it, regardless of scopes. Use it to check what a key can do.
Grant each key only the scopes it needs. A monitoring job that reads analytics needs `proxy:analytics:read` and nothing else.
## Package restrictions
A key can be restricted to specific packages (`package_ids`). A restricted key:
* only sees those packages in [`GET /v1/proxy/packages`](/api/packages/list),
* gets **404** (not 403) when requesting any other package — so the existence of packages outside the restriction is never revealed,
* has its [analytics](/api/analytics/summary) results limited to those packages, even when it passes a broader `package_ids` filter.
A key with `package_ids: null` is unrestricted and can access every package in the organization.
## Key lifecycle
Keys can manage keys, which lets you automate rotation without touching the dashboard:
* **Inspect** — [`GET /v1/api-keys/me`](/api/keys/me) shows the calling key's scopes, restriction, and rate-limit quota.
* **Create** — [`POST /v1/api-keys/`](/api/keys/create) (requires `api-keys:write`). To prevent privilege escalation, the new key's scopes must be a subset of the caller's, and its package restriction a subset of the caller's restriction.
* **Revoke** — [`DELETE /v1/api-keys/{key_id}`](/api/keys/revoke) (requires `api-keys:write`). Revocation is immediate and permanent: the key remains visible in listings with status `revoked`, but every request using it fails with 401. A key can revoke itself.
To rotate a key with zero downtime: create the replacement, deploy it, then revoke the old key.
## Best practices
* Store secrets in a secrets manager or environment variables, never in code or client-side bundles.
* Use separate keys per service or environment, so one leak doesn't require rotating everything and usage stays attributable (each key tracks `last_used_at`).
* Scope keys down: read-only keys for dashboards and monitoring, `api-keys:write` only where rotation is automated.
* Enable [two-factor authentication](/dashboard/account-billing#two-factor-authentication-2fa) on dashboard accounts that can manage API keys.
# Error handling
Source: https://developers.soax.com/api/errors
Every status code the SOAX API returns, the error response shapes, common error messages, and how to handle each one.
The SOAX API uses conventional HTTP status codes. Anything in the `2xx` range is success, `4xx` means something is wrong with the request, and `5xx` means something went wrong on the SOAX side.
## Error response shapes
Errors return a JSON object with the message under one of two keys, depending on which validation layer rejected the request. Endpoint-level checks (auth, scopes, not-found, pagination and count limits) use `detail`:
```json theme={null}
{ "detail": "Package not found" }
```
Deeper business-rule validation — connection-string rule combinations, analytics date ranges and filters, API key creation rules — and all 429s use `error`:
```json theme={null}
{ "error": "Cannot specify both 'rotate-time' and 'rotate-requests' simultaneously." }
```
The split is an implementation detail you shouldn't depend on — in your error handling, read `detail ?? error` and treat them the same. The tables below show which key each message actually uses.
**422 validation errors** (a parameter or body field failed type/format validation) are the third shape: a `detail` **array**, one entry per problem, with the location of the offending field:
```json theme={null}
{
"detail": [
{
"loc": ["query", "limit"],
"msg": "Input should be less than or equal to 100",
"type": "less_than_equal"
}
]
}
```
## Status codes
| Code | Meaning | How to handle |
| ----- | ---------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `200` | Success | — |
| `201` | Created (new API key) | Store the returned `secret` immediately — it is never shown again. |
| `204` | Success, no body (key revoked) | — |
| `400` | Invalid parameter value | Don't retry as-is. The `detail` message names the parameter and, where applicable, lists valid values. Fix the request. |
| `401` | Missing, invalid, or revoked API key | Don't retry. Check the `Authorization: Bearer ` header and the key's status. If the key was revoked, create a new one. |
| `403` | Key lacks the required scope, or your plan lacks a feature | Don't retry. Either use a key with the right scope (the `detail` names it), or — for ASN/ZIP location endpoints — upgrade to a plan with that targeting feature. |
| `404` | Resource not found | Don't retry. The resource doesn't exist, belongs to another organization, or is outside the key's package restriction (restricted keys get 404, never 403, for packages they can't see). |
| `422` | Request failed schema validation | Don't retry as-is. Fix the field named in `loc`. |
| `429` | Rate limit exceeded | Wait the number of seconds in the `Retry-After` header, then retry. See [Rate limits](/api/rate-limits). |
| `5xx` | Server error | Retry with exponential backoff. If it persists, contact support with the `x-request-id` response header value. |
Every response includes an `x-request-id` header. Log it — support can trace exactly what happened to a request from that ID.
## Common error messages
Messages under the `detail` key:
| Status | `detail` | What it means |
| ------ | --------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 401 | `Invalid API key` | No key sent, the secret is wrong, or the key was revoked. |
| 403 | `Missing required scope: ` | The key doesn't carry the scope this endpoint requires. See [scopes](/api/authentication#scopes). |
| 403 | `Feature asn_targeting is not enabled` / `Feature zip_code_targeting is not enabled` | Your plan doesn't include ASN or ZIP targeting ([ASNs](/api/locations/package-asns), [ZIP codes](/api/locations/package-zip-codes)). |
| 404 | `Package not found` | Wrong package ID, another organization's package, or outside the key's package restriction. |
| 404 | `API key not found` | The `key_id` being revoked doesn't exist in your organization. |
| 404 | `No active subscription found` | The account has no active subscription. |
| 400 | `Invalid status value: … Valid values are: active, deleted, limited, paused, suspended` | Bad `status` filter on [list packages](/api/packages/list). One value only — comma-separated lists are rejected. |
| 400 | `Count cannot exceed 1000.` | `count` on [connection strings](/api/packages/connection-string) is above the maximum. |
| 400 | `Country code '…' is not available for this package.` | A [package location endpoint](/api/locations/package-regions) was called with a country the package can't target. List valid ones via [package countries](/api/locations/package-countries). |
| 400 | `Network '…' is not available for this package. Available networks: …` | Bad `network` filter on a package location endpoint; the message lists what the package supports. |
Messages under the `error` key:
| Status | `error` | What it means |
| ------ | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| 400 | `Country '…' is not available for this proxy package.` | [Connection strings](/api/packages/connection-string) requested for a country the package can't target. |
| 400 | `Invalid network '…'. Must be one of: residential, mobile, any.` | Bad `network` value on connection strings. |
| 400 | `Country must be specified if region, city, ISP, ASN, or ZIP is provided.` | Finer-grained targeting always needs `country`. |
| 400 | `Cannot specify both 'rotate-time' and 'rotate-requests' simultaneously.` | Pick one rotation mode. |
| 400 | `The 'retries' parameter is required when onerror='retry'.` | `on-error=retry` needs `retries`; `retries` is only valid with `on-error=retry`. |
| 400 | `Session ID must be an alphanumeric lowercase string up to 16 characters.` | `session` must match `^[a-z0-9]{1,16}$`. |
| 400 | `bind=node is incompatible with prefer=lookalike.` / `…with onerror=replace.` | A bound session can't be replaced, so replacement-based options don't apply. |
| 400 | `Date range cannot be more than 1 year` | An [analytics](/api/analytics/summary) query spans more than 365 days. |
| 400 | `hour interval requires a date range less than 24 hours` | See interval constraints in [Rate limits](/api/rate-limits#other-usage-constraints); `day` and `month` have analogous messages. |
| 400 | `Invalid scopes: … Valid scopes are: …` | [Creating a key](/api/keys/create) with an unknown scope. |
| 400 | `Requested scopes exceed the calling key's own scopes` | [Creating a key](/api/keys/create) with more scopes than the caller has. |
| 429 | `Rate limit exceeded: …` | Too many requests. Wait `Retry-After` seconds. |
## Handling errors in code
A pattern that covers the cases worth automating — retry `429` and `5xx`, surface everything else:
```python Python theme={null}
import time
import requests
def soax_request(method, url, *, api_key, max_attempts=5, **kwargs):
headers = {"Authorization": f"Bearer {api_key}"}
for attempt in range(max_attempts):
resp = requests.request(method, url, headers=headers, **kwargs)
if resp.status_code == 429:
time.sleep(int(resp.headers.get("Retry-After", 1)))
continue
if resp.status_code >= 500:
time.sleep(min(2 ** attempt, 30)) # exponential backoff, capped
continue
if resp.status_code >= 400:
body = resp.json()
message = body.get("detail") or body.get("error") or body
raise RuntimeError(
f"SOAX API error {resp.status_code}: {message} "
f"(x-request-id: {resp.headers.get('x-request-id')})"
)
return resp.json() if resp.status_code != 204 else None
raise RuntimeError(f"Gave up after {max_attempts} attempts: {url}")
```
```javascript Node.js theme={null}
async function soaxRequest(url, { apiKey, maxAttempts = 5, ...init } = {}) {
for (let attempt = 0; attempt < maxAttempts; attempt++) {
const resp = await fetch(url, {
...init,
headers: { Authorization: `Bearer ${apiKey}`, ...init.headers },
});
if (resp.status === 429) {
const retryAfter = Number(resp.headers.get("Retry-After") ?? 1);
await new Promise((r) => setTimeout(r, retryAfter * 1000));
continue;
}
if (resp.status >= 500) {
await new Promise((r) => setTimeout(r, Math.min(2 ** attempt * 1000, 30000)));
continue;
}
if (!resp.ok) {
const body = await resp.json().catch(() => ({}));
const message = body.detail ?? body.error ?? JSON.stringify(body);
throw new Error(
`SOAX API error ${resp.status}: ${message} ` +
`(x-request-id: ${resp.headers.get("x-request-id")})`
);
}
return resp.status === 204 ? null : resp.json();
}
throw new Error(`Gave up after ${maxAttempts} attempts: ${url}`);
}
```
These are errors from the **API** (`api.platform.soax.com`). Errors from the **proxy gateway** (`proxy.soax.com:1337`, e.g. `407 AUTH_FAILED`) are a different system with different codes — see [Proxy error codes](/troubleshooting/error-codes).
# Create an API key
Source: https://developers.soax.com/api/keys/create
api/openapi.json POST /v1/api-keys/
Create a new API key. To prevent privilege escalation, the new key's `scopes` must be a subset of the calling key's scopes, and `package_ids` (if provided) must be a subset of the calling key's package restriction. If `package_ids` is omitted, the new key inherits the calling key's restriction.
The response includes the plaintext `secret` — this is the **only** time it is ever returned. Store it immediately in a secure location.
Requires the `api-keys:write` scope.
# List API keys
Source: https://developers.soax.com/api/keys/list
api/openapi.json GET /v1/api-keys/
Retrieve all API keys belonging to your organization, including revoked ones. Key secrets are never returned — only metadata. Requires the `api-keys:read` scope.
# Get current API key
Source: https://developers.soax.com/api/keys/me
api/openapi.json GET /v1/api-keys/me
Retrieve details about the API key used to authenticate the request: its scopes, package restriction, organization ID, and current rate-limit quota. Useful as a first call to verify a key works, and to check remaining rate-limit quota. This endpoint requires no scope — any active key can call it. The key secret is never returned.
# Revoke an API key
Source: https://developers.soax.com/api/keys/revoke
api/openapi.json DELETE /v1/api-keys/{key_id}
Revoke an API key belonging to your organization. Revocation is a soft delete: the key keeps appearing in `GET /v1/api-keys/` with status `revoked`, but any request using it is rejected with 401 from that point on. A key can revoke itself. Revocation cannot be undone — create a new key instead.
Requires the `api-keys:write` scope.
# List proxy countries
Source: https://developers.soax.com/api/locations/countries
api/openapi.json GET /v1/proxy/locations/countries
List every country available on the SOAX network with its tier, regardless of package. To see what a specific package can target, use the package countries endpoint instead.
Requires the `proxy:packages:read` scope.
# List package ASNs
Source: https://developers.soax.com/api/locations/package-asns
api/openapi.json GET /v1/proxy/packages/{package_id}/locations/countries/{cc}/asns
List ASNs available in a country for this package. Use `code` values as the `asn` parameter of the connection-string endpoint.
**Feature gate**: returns 403 if your plan does not include ASN targeting.
Requires the `proxy:packages:read` scope.
# List package cities
Source: https://developers.soax.com/api/locations/package-cities
api/openapi.json GET /v1/proxy/packages/{package_id}/locations/countries/{cc}/cities
List cities available in a country (optionally narrowed to a region) for this package. Use `code` values as the `city` parameter of the connection-string endpoint.
Requires the `proxy:packages:read` scope.
# List package countries
Source: https://developers.soax.com/api/locations/package-countries
api/openapi.json GET /v1/proxy/packages/{package_id}/locations/countries
List the countries this package can route traffic through, with the tier of each country. Use the returned `code` values as the `country` parameter of the connection-string endpoint.
Requires the `proxy:packages:read` scope.
# List package ISPs
Source: https://developers.soax.com/api/locations/package-isps
api/openapi.json GET /v1/proxy/packages/{package_id}/locations/countries/{cc}/isps
List ISPs available in a country (optionally narrowed by region and city) for this package. Use `code` values as the `isp` parameter of the connection-string endpoint.
Requires the `proxy:packages:read` scope.
# List package regions
Source: https://developers.soax.com/api/locations/package-regions
api/openapi.json GET /v1/proxy/packages/{package_id}/locations/countries/{cc}/regions
List regions available in a country for this package. `rank` orders locations by available pool size (1 = largest) and `volume_level` is a coarse 1–5 indicator of relative IP volume. Use `code` values as the `region` parameter of the connection-string endpoint.
Requires the `proxy:packages:read` scope.
# List package ZIP codes
Source: https://developers.soax.com/api/locations/package-zip-codes
api/openapi.json GET /v1/proxy/packages/{package_id}/locations/countries/{cc}/zip-codes
List ZIP codes available in a country (optionally narrowed by region and city) for this package. Use `code` values as the `zip` parameter of the connection-string endpoint.
**Feature gate**: returns 403 if your plan does not include ZIP targeting.
Requires the `proxy:packages:read` scope.
# List proxy tiers
Source: https://developers.soax.com/api/locations/tiers
api/openapi.json GET /v1/proxy/locations/tiers
List the proxy tiers SOAX offers. Tiers group countries by pool quality and pricing; each country belongs to one tier.
Requires the `proxy:packages:read` scope.
# API overview
Source: https://developers.soax.com/api/overview
Programmatic access to your SOAX account: packages, connection strings, locations, usage analytics, and API key management over a JSON REST API.
The SOAX API gives you programmatic access to everything you'd otherwise manage in the [dashboard](https://platform.soax.com): list and inspect your proxy packages, generate ready-to-use connection strings, browse available locations, pull usage analytics, and manage API keys — all without logging in.
The API manages your **account**. It is separate from the proxy gateway (`proxy.soax.com:1337`) that carries your actual proxy traffic. You authenticate to the API with an **API key** (`s_live_…`); you authenticate to the proxy gateway with a **package key** (a 10-character password, one per package). See [Authentication](/api/authentication) for the difference.
## Base URL
All endpoints live under a single base URL and are versioned under `/v1`:
```
https://api.platform.soax.com/v1
```
Requests and responses are JSON over HTTPS. Use paths exactly as documented — the API keys collection paths end with a trailing slash (`/v1/api-keys/`), and calling them without it triggers a 307 redirect that not every HTTP client follows with the `Authorization` header intact.
## Authentication
Every request must carry an API key in the `Authorization` header:
```bash theme={null}
curl https://api.platform.soax.com/v1/api-keys/me \
-H "Authorization: Bearer s_live_YOUR_API_KEY"
```
Keys are created in the dashboard under [**Settings → API keys**](https://platform.soax.com/settings/api-keys) and carry **scopes** that control which endpoints they can call. See [Authentication](/api/authentication) for the full scope reference.
## Endpoints at a glance
| Group | What it covers |
| ----------------------------------- | ------------------------------------------------------------------------------------ |
| [API Keys](/api/keys/me) | Inspect the current key, list, create, and revoke API keys |
| [Packages](/api/packages/list) | List and inspect proxy packages, build connection strings, rotate package passwords |
| [Locations](/api/locations/tiers) | Tiers, countries, regions, cities, ISPs, ASNs, and ZIP codes available for targeting |
| [Account](/api/account/get) | Account info, credit balances, subscription details |
| [Analytics](/api/analytics/summary) | Usage summaries, traffic breakdowns, credits burned |
## Pagination
`GET /v1/proxy/packages` uses cursor-based pagination. Each page returns `has_more` and `next_cursor`; pass `next_cursor` as the `cursor` query parameter to fetch the next page, and stop when `has_more` is `false`. Other list endpoints return complete lists without pagination.
## Rate limits
Each API key can make **5 requests per second**, and each client IP **500 requests per minute**. Exceeding either returns `429`. See [Rate limits](/api/rate-limits) for details and retry guidance.
## Errors
Errors use conventional HTTP status codes with a JSON body describing the problem. See [Error handling](/api/errors) for every status code, the response shapes, and how to handle each case.
## OpenAPI specification
The API is described by an OpenAPI 3.1 specification, useful for generating clients or importing into tools like Postman:
```
https://api.platform.soax.com/v1/openapi.json
```
## Next steps
Create an API key and make your first request in a few minutes.
API keys, scopes, package restrictions, and key lifecycle.
Status codes, error bodies, and how to handle each.
Limits, the 429 response, and how to check your remaining quota.
# Get connection strings
Source: https://developers.soax.com/api/packages/connection-string
api/openapi.json GET /v1/proxy/packages/{package_id}/connection-string
Build ready-to-use proxy connection strings for a package, with targeting and session rules encoded for you. The returned credentials are used against the proxy gateway (`proxy.soax.com:1337`) — not against this API.
Location parameters are validated against what the package can actually target: an unavailable country, region, city, ISP, ASN, or ZIP returns 400. `region`, `city`, `isp`, `asn`, and `zip` all require `country` to be set.
Rotation and error handling: `rotate-time` and `rotate-requests` are mutually exclusive; `on-error=retry` requires `retries`, and `retries` is only valid with `on-error=retry`; `bind=node` is incompatible with `prefer=lookalike` and `on-error=replace`. When any rotation/error/bind rule is used (or `count` > 1) and no `session` is given, a session ID is generated automatically; with `count` > 1 the session IDs are numbered so each connection string gets its own IP.
For packages using IP allowlist authentication, rules are encoded in the hostname (HTTPS only), `username` is empty, and `password` is `null`.
Requires the `proxy:packages:read` scope.
# Get a package
Source: https://developers.soax.com/api/packages/get
api/openapi.json GET /v1/proxy/packages/{package_id}
Retrieve full configuration for one proxy package: status, network types, tiers, traffic usage (in bytes), the package password used for proxy authentication, connection limits, allowed IPs, port and target rules, and enabled protocols.
If the API key is restricted to specific packages, requesting a package outside the restriction returns 404 (not 403), so the existence of other packages is not revealed.
Requires the `proxy:packages:read` scope.
# List packages
Source: https://developers.soax.com/api/packages/list
api/openapi.json GET /v1/proxy/packages
Retrieve a paginated list of proxy packages accessible to the API key. If the key is restricted to specific packages, only those are returned. Use cursor-based pagination: pass the `next_cursor` value from one response as the `cursor` parameter of the next request until `has_more` is `false`. Traffic values are in bytes.
Requires the `proxy:packages:read` scope.
# Rotate package password
Source: https://developers.soax.com/api/packages/rotate-password
api/openapi.json POST /v1/proxy/packages/{package_id}/rotate-password
Generate a new package password (the 10-character key used to authenticate proxy requests — not the API key). The old password stops working immediately, so update every client that connects through this package. The new plaintext password is returned only in this response.
Returns 400 if the package is deleted or suspended.
Requires the `proxy:packages:write` scope.
# API quickstart
Source: https://developers.soax.com/api/quickstart
Create an API key, verify it, list your packages, and generate a proxy connection string — with curl, Python, and Node.js examples.
This guide takes you from zero to a working integration: you'll create an API key, verify it works, list your proxy packages, and generate a connection string you can plug straight into your proxy client.
## 1. Create an API key
1. Log in to the [dashboard](https://platform.soax.com).
2. Go to [**Settings → API keys**](https://platform.soax.com/settings/api-keys).
3. Click **Generate new API key**, give it a name, and pick its scopes. For this walkthrough you need at least `proxy:packages:read`.
4. Optionally restrict the key to specific packages — a restricted key can only see and act on those packages.
5. Copy the secret. It starts with `s_live_` and is **shown only once** — store it somewhere secure right away.
Treat the secret like a password. Don't commit it to source control or embed it in client-side code. If a key leaks, [revoke it](/api/keys/revoke) and create a new one.
## 2. Verify the key
Call [`GET /v1/api-keys/me`](/api/keys/me) — it needs no scope and echoes back what the key can do:
```bash theme={null}
curl https://api.platform.soax.com/v1/api-keys/me \
-H "Authorization: Bearer s_live_YOUR_API_KEY"
```
```json theme={null}
{
"id": "3f8a2c1e-5b6d-4e7f-8a9b-0c1d2e3f4a5b",
"scopes": ["proxy:packages:read"],
"package_ids": null,
"org_id": "0b9c8d7e-6f5a-4b3c-2d1e-0f9a8b7c6d5e",
"rate_limit": { "configured_limit": "5/second", "remaining_quota": 4 }
}
```
If you get `401 {"detail": "Invalid API key"}` instead, check that the header is exactly `Authorization: Bearer ` and that the key hasn't been revoked.
## 3. List your packages
```bash curl theme={null}
curl https://api.platform.soax.com/v1/proxy/packages \
-H "Authorization: Bearer s_live_YOUR_API_KEY"
```
```python Python theme={null}
import requests
API_KEY = "s_live_YOUR_API_KEY"
BASE_URL = "https://api.platform.soax.com/v1"
resp = requests.get(
f"{BASE_URL}/proxy/packages",
headers={"Authorization": f"Bearer {API_KEY}"},
)
resp.raise_for_status()
for pkg in resp.json()["data"]:
print(pkg["id"], pkg["name"], pkg["status"])
```
```javascript Node.js theme={null}
const API_KEY = "s_live_YOUR_API_KEY";
const BASE_URL = "https://api.platform.soax.com/v1";
const resp = await fetch(`${BASE_URL}/proxy/packages`, {
headers: { Authorization: `Bearer ${API_KEY}` },
});
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
const { data } = await resp.json();
for (const pkg of data) console.log(pkg.id, pkg.name, pkg.status);
```
```json theme={null}
{
"data": [
{
"id": "b2f0c3d4-e5a6-4b7c-8d9e-0f1a2b3c4d5e",
"name": "Residential - EU scraping",
"status": "active",
"status_reason": null,
"created_at": "2026-03-15T12:00:00Z",
"types": ["wifi"],
"tiers": [1, 2, 3],
"allowed_countries": null,
"traffic_limit": 500000000000,
"traffic_spent": 120000000000,
"traffic_left": 380000000000
}
],
"has_more": false,
"next_cursor": null
}
```
Grab the `id` of the package you want to use — you'll need it in the next step.
## 4. Generate a connection string
The [connection-string endpoint](/api/packages/connection-string) builds proxy credentials for you, with targeting rules already encoded — no need to construct the username format by hand:
```bash curl theme={null}
curl "https://api.platform.soax.com/v1/proxy/packages/b2f0c3d4-e5a6-4b7c-8d9e-0f1a2b3c4d5e/connection-string?protocol=http&country=us" \
-H "Authorization: Bearer s_live_YOUR_API_KEY"
```
```python Python theme={null}
resp = requests.get(
f"{BASE_URL}/proxy/packages/{package_id}/connection-string",
headers={"Authorization": f"Bearer {API_KEY}"},
params={"protocol": "http", "country": "us"},
)
resp.raise_for_status()
conn = resp.json()[0]
print(conn["uri"])
```
```javascript Node.js theme={null}
const params = new URLSearchParams({ protocol: "http", country: "us" });
const csResp = await fetch(
`${BASE_URL}/proxy/packages/${packageId}/connection-string?${params}`,
{ headers: { Authorization: `Bearer ${API_KEY}` } }
);
if (!csResp.ok) throw new Error(`HTTP ${csResp.status}`);
const [conn] = await csResp.json();
console.log(conn.uri);
```
```json theme={null}
[
{
"uri": "http://country-us:aB3dE5fG7h@proxy.soax.com:1337",
"username": "country-us",
"password": "aB3dE5fG7h",
"host": "proxy.soax.com",
"port": 1337
}
]
```
## 5. Use it
The returned `uri` is a standard proxy URL — pass it to any HTTP client:
```bash curl theme={null}
curl -x "http://country-us:aB3dE5fG7h@proxy.soax.com:1337" \
https://checker.soax.com/api/ipinfo
```
```python Python theme={null}
proxies = {"http": conn["uri"], "https": conn["uri"]}
ip = requests.get("https://checker.soax.com/api/ipinfo", proxies=proxies)
print(ip.json())
```
You're routed through a US IP. From here you can add sessions, rotation, and finer targeting (region, city, ISP, ASN, ZIP) via the same endpoint's query parameters.
## Next steps
All targeting, session, and rotation parameters.
What each scope unlocks, and how to manage keys programmatically.
Pull traffic and credit usage into your own reporting.
Stay under the limits and handle 429s gracefully.
# Rate limits
Source: https://developers.soax.com/api/rate-limits
SOAX API rate limits: 5 requests/second per API key and 500 requests/minute per IP, how the 429 response looks, how to check remaining quota, and other usage constraints.
The SOAX API enforces two independent rate limits, and a request must pass **both**:
| Limit | Applies to | Window |
| ------------------------- | ---------------------- | --------------------- |
| **5 requests / second** | each API key | fixed 1-second window |
| **500 requests / minute** | each client IP address | fixed 1-minute window |
Two keys used from the same machine share the IP budget; the same key used from two machines shares the key budget. Limits are fixed-window: the counter resets at each window boundary rather than sliding continuously.
## The 429 response
Exceeding either limit returns `429 Too Many Requests`:
```json theme={null}
{ "error": "Rate limit exceeded: 5 per 1 second" }
```
The response carries standard rate-limit headers telling you exactly when to retry:
| Header | Meaning |
| --------------------- | -------------------------------------------------- |
| `Retry-After` | Seconds to wait before retrying (at least 1) |
| `RateLimit-Limit` | The bucket size (e.g. `5`) |
| `RateLimit-Remaining` | Requests left in the current window (`0` on a 429) |
| `RateLimit-Reset` | Unix timestamp when the window resets |
Honor `Retry-After` rather than guessing:
```python theme={null}
import time, requests
def get_with_retry(url, headers, max_attempts=5):
for _ in range(max_attempts):
resp = requests.get(url, headers=headers)
if resp.status_code != 429:
return resp
time.sleep(int(resp.headers.get("Retry-After", 1)))
raise RuntimeError("still rate limited after retries")
```
For sustained workloads, spacing requests (a client-side throttle at \~4 requests/second per key) is more effective than reacting to 429s.
## Checking your quota
Every authenticated response — not just 429s — includes the `RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset`, and `Retry-After` headers for your key's bucket, so you can track quota passively from responses you're already making.
If you want it in a body instead, [`GET /v1/api-keys/me`](/api/keys/me) returns the same information (note that this call itself counts against the limit):
```json theme={null}
{
"rate_limit": {
"configured_limit": "5/second",
"remaining_quota": 3
}
}
```
## Other usage constraints
Beyond request rates, individual endpoints enforce these caps:
| Constraint | Value | Where |
| ------------------------------ | -------------------------------------- | --------------------------------------------------------- |
| Packages per page | `limit` 1–100 (default 20) | [List packages](/api/packages/list) |
| Connection strings per request | `count` 1–1000 | [Get connection strings](/api/packages/connection-string) |
| Analytics date range | at most 365 days | all [analytics endpoints](/api/analytics/summary) |
| `interval=hour` | range under 24 hours | [Traffic breakdown](/api/analytics/traffic) |
| `interval=day` | range between 24 hours and 180 days | [Traffic breakdown](/api/analytics/traffic) |
| `interval=month` | range of at least 60 days | [Traffic breakdown](/api/analytics/traffic) |
| `session` ID format | lowercase letters/digits, max 16 chars | [Get connection strings](/api/packages/connection-string) |
## Tips for staying under the limits
* **Cache reference data.** Tiers, countries, and package location lists change rarely — fetch them once and refresh occasionally, don't call them per request.
* **Batch connection strings.** One call with `count=100` gives you 100 session-distinct connection strings; you don't need 100 calls.
* **Use one key per service, not per request.** Each key gets its own 5 req/s budget, but creating keys in a hot path is an anti-pattern — create them ahead of time.
These limits apply to the management API only. Proxy traffic through `proxy.soax.com:1337` is governed by your package's own limits (concurrent connections, traffic volume), not by these API rate limits.
# Account & Billing
Source: https://developers.soax.com/dashboard/account-billing
Manage your subscription, payment methods, and invoices — and understand how traffic and overages are calculated.
Everything you need to handle billing lives in **Settings**, accessible from the top navigation bar in the dashboard. This page covers what's there and how it works, so you can manage your account without needing to open a support ticket.
## Your subscription
Go to [**Settings → Billing**](https://platform.soax.com/settings/billing) to see your current plan.
You'll find:
* **Plan name and status** — whether your subscription is active, paused, or cancelled
* **Subscription ID** — useful if you ever need to reference your account with support
* **Plan limits** — your monthly credit allowance, seat count, and number of packages
* **Next billing date** — when your next charge will be processed
To make changes — upgrade, downgrade, or cancel — click **Manage subscription** in the top right of the subscription card.
## Invoices
Your invoice history is at the bottom of the **Billing** tab.
Each invoice shows:
* **Invoice ID** — a unique reference number
* **Date and due date**
* **Amount charged**
* **Status** — Paid, Unpaid, or Pending
You can filter by status or date range. To download an invoice, click the download icon on the right. To preview it in the browser first, click **Preview**.
## Payment methods
Go to **[Settings → Billing](https://platform.soax.com/settings/billing) → Payment methods** to add or update a payment method. SOAX supports four payment options:
* **Credit card** — Visa, Mastercard, Amex. Processed instantly. Adding a card enables automatic payments on your billing date.
* **Cryptocurrency** — BTC, ETH, USDT, USDC, SOL, and more. Takes 5–30 minutes to process. Requires KYC verification before use — see Verification below.
* **Bank transfer** — Wire transfer. Manual checkout; takes 1–3 business days to settle.
* **Postpaid billing** — Enterprise plan only. Your subscription is billed upfront; any overage is invoiced at the end of each month.
## Billing information
Go to **[Settings → Billing](https://platform.soax.com/settings/billing) → Edit billing info** to update your company name, billing address, and VAT ID. This is separate from your account email — invoices are addressed using whatever you set here, so make sure it reflects your company or finance team's details.
### VAT
VAT applies to customers in the EU and UK. Customers outside these regions aren't charged VAT.
* **If you're a business (B2B) with a valid VAT ID** — add it to your billing details and your invoices will be issued under the reverse charge mechanism. SOAX won't charge you VAT; you report it to your local tax office instead. If your VAT ID is missing or invalid, VAT is added automatically at your country's standard rate.
* **If you're an individual (B2C)** — VAT is applied automatically based on your billing country at the local rate.
VAT is shown during checkout before you confirm payment. You can add or update your VAT ID at any time from **Edit billing info**.
## How credits work
SOAX measures usage in **credits**. 1 credit = \$1, and each credit buys a certain amount of proxy traffic depending on which country tier you're targeting.
### Proxy pricing by country tier
The price per GB varies by location. Higher-tier countries (US, UK, Western Europe) cost more per GB than lower-tier ones.
| Tier | Builder (\$200/mo) | Team (\$500/mo) | Scale (\$1500/mo) | Enterprise |
| ---------------- | -------------------- | ------------------ | ------------------ | -------------- |
| Tier 1 countries | \$3.00/GB \~66.7 GB | \$2.20/GB \~227 GB | \$1.50/GB \~1.0 TB | from \$0.50/GB |
| Tier 2 countries | \$2.25/GB \~88.9 GB | \$1.65/GB \~303 GB | \$1.15/GB \~1.3 TB | from \$0.35/GB |
| Tier 3 countries | \$1.20/GB \~166.7 GB | \$0.90/GB \~555 GB | \$0.60/GB \~2.5 TB | from \$0.25/GB |
The approximate GB figures are what your included credits cover at that tier's rate. Actual usage will vary depending on which countries you target.
### Credit expiry
Credits are added to your prepaid credit balance in separate blocks. Each block has its own expiry date, which does not change.
Credits added on Sandbox or a monthly plan expire 60 days after they are added. Credits added on an annual plan expire 12 months after they are added.
Renewing, changing, or cancelling your plan does not change the expiry dates of credits already in your balance. If credits are available in several unexpired blocks, we use the block that expires first.
### What counts as usage
Traffic is counted when a request is successfully routed through a SOAX proxy node. Data transferred in both directions (request + response) counts toward your usage.
Traffic is **not** counted for:
* Failed connection attempts (e.g. authentication errors, timeouts before a connection is established)
* Internal dashboard activity
Check your current usage at any time in [**Settings → Credits**](https://platform.soax.com/settings/credits) or on the [Usage breakdown](https://platform.soax.com/usage) dashboard.
## How overages work
When you run out of included credits, additional usage is charged at your plan's per-GB rate for the country tier you're targeting.
Here's how it works in practice:
1. Your plan includes a set number of credits (shown under **Plan limits** in the subscription card).
2. Once you use them up, requests continue to go through — SOAX doesn't cut off your traffic mid-operation.
3. Additional usage is billed at the same per-GB rate as your plan.
4. The overage charge appears as a separate line on your next invoice.
Postpaid billing — where you pay after usage rather than upfront — is only available on the Enterprise plan.
If you're consistently going over your credits, upgrading your plan reduces your per-GB rate significantly. For example, Tier 1 traffic drops from $3.00/GB on Builder to $1.50/GB on Scale — so the same volume costs half as much.
## Verification
Identity verification is found in [**Settings → Profile → Verification**](https://platform.soax.com/settings/profile).
### Personal identity (KYC)
KYC verification unlocks:
* Crypto payments
* Restricted domain and port access
To start, click **Start verification** under [**Profile → Verification**](https://platform.soax.com/settings/profile). You'll need to upload a government-issued ID. Once your documents are uploaded, the status changes to **Ready to verify** while our team processes them.
## Team and permissions
Every account has two roles: **Owner** and **Member**. The Owner controls billing, packages, and account configuration; Members use the packages they're given. This keeps spend and configuration under the Owner's control while letting the rest of the team connect and work.
| Capability | Owner | Member |
| -------------------------------------------------------- | ----- | ------ |
| Connect through assigned packages | Yes | Yes |
| View usage and analytics | Yes | Yes |
| Create, edit, pause, or delete packages | Yes | No |
| Set or change package limits (RPS, connections, traffic) | Yes | No |
| Purchase credits | Yes | No |
| Manage the subscription, payment methods, and invoices | Yes | No |
| Request access to restricted domains or ports | Yes | No |
| Manage team members and account security | Yes | No |
If you're a Member and need to do something only Owner can, ask an Owner on your team.
## Account security
### Two-factor authentication (2FA)
2FA is managed in [**Settings → Profile → Authentication**](https://platform.soax.com/settings/profile). It's disabled by default. To enable it, click **Add OTP device** and follow the setup steps with an authenticator app (e.g. Google Authenticator or Authy).
We recommend enabling 2FA, especially if your account has API keys or active billing methods attached.
### Active sessions
You can see all devices currently logged into your account under [Settings → Ptofile → Authentication → Active sessions](https://platform.soax.com/settings/profile). If you see a session you don't recognize, click **Revoke** to sign it out immediately.
# Package Management
Source: https://developers.soax.com/dashboard/package-management
Create, configure, and manage proxy packages from the dashboard. Packages are the unit of access and billing — credentials, limits, and entitlements all live on a package.
A **proxy package** is your entry point to the SOAX network. Every request you make runs inside a package and inherits its credentials, limits, and entitlements.
You can create as many packages as you need — one per environment, one per team, one per workload — and manage them independently. All packages share your organization's credit pool, but each package controls who can spend from it and how.
Only organization **Owners** can create, edit, pause, or delete packages and change their limits. **Members** can connect through the packages assigned to them, but can't change package configuration. See [Team and permissions](/dashboard/account-billing#team-and-permissions).
## What's on a package
| Attribute | What it controls |
| -------------------------------- | --------------------------------------------------------- |
| **Package key** | Authentication credential (used as the proxy password). |
| **Network types** | Which networks are allowed: residential, mobile, or both. |
| **RPS limit** | Maximum requests per second for this package. |
| **Concurrent connections limit** | Maximum simultaneous connections. |
| **Traffic limit** | Optional cap on GB consumed by this package. |
| **IP allowlist** | Authorized client IPs for IP Auth. |
| **Feature entitlements** | Premium features enabled on the package. |
Limits are enforced at two levels. Each package has its own RPS and concurrent connection limit. There's also a customer-level ceiling across all your packages, so creating more packages doesn't sidestep the global limit.
## Creating a package
1. In the dashboard, open [**Packages**](https://platform.soax.com/packages) in the sidebar.
2. Click **Create package**.
3. Set the package name, allowed network types, and any optional traffic limit.
4. Save. The package key is generated and shown on the package detail page.
The package is immediately available for use. Open Quick Connect on the package page to start building a connection string.
## Managing the package key
The package key is the password half of every connection string:
```text theme={null}
{rules}:{package_key}@proxy.soax.com:1337
```
Keys look like `pk_abc123…`. They're shown on the package detail page; copy the value exactly.
### Regenerating a key
If a key is leaked or you want to rotate it on schedule, regenerate it from the package detail page. The old key stops working immediately, so update every integration that uses it before regenerating.
## Network types
Residential and mobile are both enabled by default on new packages. You can disable one if you want a package restricted to a single network type — for example, to give a team residential-only access, or to keep a high-volume scraping workload on residential without accidentally consuming mobile credits.
The network type is selected at runtime via the `network` rule (`network-res`, `network-mob`, `network-any`, or combinations like `network-res_mob`).
If you don't include a `network` rule, the package's default is used (`res`).
## Limits and quotas
### RPS and concurrent connections
Each package has an RPS limit and a concurrent-connections limit, both visible on the package detail page. On the Sandbox plan, the concurrent-connections limit is **4,000 per proxy gateway**. Hitting either limit returns:
```text theme={null}
HTTP/1.1 429 Too Many Requests
X-SOAX-Error: RATE_LIMIT_EXCEEDED
```
If your workload needs more headroom, ask an organization Owner to raise the package's limits. The customer-level ceiling across all packages is tied to your plan — to lift that, an Owner can contact support to have the limits evaluated and increased.
### Traffic limit
The optional traffic limit lets you cap GB consumption on a single package without affecting other packages. Useful for ring-fencing a team or an experimental workload from the rest of the organization's budget.
Hitting the cap returns:
```text theme={null}
HTTP/1.1 429 Too Many Requests
X-SOAX-Error: TRAFFIC_LIMIT_EXCEEDED
```
Update the cap from the package settings.
## IP allowlist (for IP Auth)
If you want to authenticate by IP instead of sending the package key on every request, add your client's outbound public IP to the package's IP allowlist, found in the package's **Access** tab (`platform.soax.com/packages//access`).
* The IP added must be the public outbound IP that SOAX sees, not your local/private IP. Check it without a proxy at [checker.soax.com/api/ipinfo](https://checker.soax.com/api/ipinfo).
* You can keep both methods active simultaneously. Adding an IP to the allowlist doesn't disable username/password auth.
* IP Auth works with HTTPS in Quick Connect (rules in the subdomain, subject to the 63-character DNS label limit). HTTP and SOCKS5 can't carry rules under IP Auth — use username/password authentication for those.
See [Authentication](/getting-started/authentication) for full setup.
## Multiple packages
Common reasons to split workloads into multiple packages:
* **Per environment** — separate packages for staging and production, so usage and limits don't cross over.
* **Per team or customer** — track traffic by team in [Usage breakdown](https://platform.soax.com/usage), and apply different limits to each.
* **Per workload** — isolate a high-volume scrape from interactive use so one can't starve the other on shared concurrency.
* **Per network type** — restrict one package to residential and another to mobile to make billing attribution clean.
All packages share the organization's credit pool. The split only controls how that pool is allocated and tracked, not how much credit you have.
## Pausing and deleting a package
* **Pause.** Temporarily disable a package without losing its settings. Requests fail with `407 PACKAGE_SUSPENDED` until resumed.
* **Delete.** Permanently remove the package. The package key is invalidated immediately. There's no undo.
## Where to look next
* **Current usage** for a package is in [Usage breakdown](https://platform.soax.com/usage), filtered by package.
* **Errors against the package** surface in your application logs via [`X-SOAX-Error`](/troubleshooting/error-codes) response headers.
* **Connection string format and rule reference** is in [Authentication](/getting-started/authentication), [Residential proxies](/proxies/residential), and [Mobile proxies](/proxies/mobile).
## Next steps
Build a connection string for this package without memorising the rules format.
Username/password and IP Auth, end-to-end.
Per-package traffic, credits, and country-tier breakdowns.
Subscription, payments, invoices, and credits.
# Quick Connect
Source: https://developers.soax.com/dashboard/quick-connect
Build SOAX connection strings without memorising the rules format. Quick Connect is the dashboard's connection-string builder for residential and mobile proxies.
Quick Connect is the dashboard's connection-string builder. You pick what you want — country, session type, rotation, error handling — and the dashboard generates the connection string you paste into your code or HTTP client.
It's also the name of the underlying mode. Putting rules directly into the connection string (`country-us-session-job1-rotate-timed_300:pk_abc123@proxy.soax.com:1337`) is "Quick Connect" no matter where you typed the string. The dashboard tool just makes building one easier.
## When to use Quick Connect
Quick Connect covers everything most integrations need:
* Rotating proxies (no session) and ephemeral sessions
* Full filtering (country, region, city, ISP, ASN, zip, network)
* Rotation (`rotate-timed_N`, `rotate-requests_N`)
* Error handling (`onerror-replace`, `onerror-retry_N`, `onerror-fail`)
* Routing preferences (`prefer-lookalike`)
* Node binding (`bind-node`)
* Unlimited concurrent sessions
## Opening Quick Connect
In the dashboard, click [**Packages**](https://platform.soax.com/packages) in the sidebar and open the package you want to use. The Quick Connect panel is on the package page.
## Building a connection string
Quick Connect walks you through four groups of options, which match the four categories of rules under the hood:
### 1. Filtering — which nodes are eligible
| Setting | What it does |
| --------------------- | -------------------------------------------------------------------------------------------------------------------- |
| **Country** | Limit to one country (lowercase ISO 3166-1 alpha-2, e.g. `us`, `gb`, `de`). Pick "any" for the full global pool. |
| **Region / state** | Narrow to a region within the chosen country. |
| **City** | Narrow further to a specific city. |
| **ISP / Carrier** | Pick a specific ISP (residential) or mobile carrier. Available ISP/carrier names are listed in the package settings. |
| **ASN** | Target a specific autonomous system number. |
| **Zip / postal code** | Target a postal code (available on Scale and Enterprise plans). |
| **Network** | Residential (`res`), mobile (`mob`), or a combination. Defaults to `res`. |
The more specific your filter combination, the smaller the eligible pool. If you target a small city and a specific ISP, you may see slower allocation or `NODE_NOT_FOUND` errors. Start broad and narrow only when you need to.
### 2. Session — rotating or held
| Setting | What it does |
| ------------ | --------------------------------------------------------------------------- |
| **Rotating** | No `session` parameter. Every request gets a new node. |
| **Session** | Adds `session-{id}`. All requests with that session ID reuse the same node. |
Session IDs are letters, digits, and underscores, up to 32 characters. Ephemeral sessions expire after **60 seconds of inactivity**. Rules are locked on the first request — sending the same session ID with different rules returns `409 SESSION_PARAMS_MISMATCH`. To change rules, use a new session ID.
### 3. Rotation — when the node is allowed to change
Only meaningful with a session.
| Setting | What it does |
| ----------------- | ------------------------------------------------------------------- |
| **Timed** | Rotate after N seconds. Accepts `s`, `m`, `h` suffixes. Max 1 week. |
| **Request count** | Rotate after N requests. Max 1,000,000. |
| **None** | Keep the same node until error, filter mismatch, or expiry. |
### 4. Error handling — what happens on node failure
| Setting | What it does |
| ----------------------- | --------------------------------------------------------------------------------------- |
| **Replace** *(default)* | Get a new eligible node and continue. |
| **Retry on same node** | `onerror-retry_N`, retries up to N times (max 10) on the current node before replacing. |
| **Fail** | Return the error to your client. No retry, no replacement. |
SOAX can only see infrastructure failures (node offline, connection refused, timeout). HTTP status codes from the target — 403, 429, CAPTCHA pages — are inside the HTTPS tunnel and aren't visible to SOAX. Detecting and responding to those is your application's job. See [CAPTCHA & ban rates](/troubleshooting/captcha-ban-rates).
### Routing and binding (optional)
| Setting | What it does |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Prefer lookalike** | `prefer-lookalike`. On any node selection (allocation, rotation, replacement), prefer a node similar to the previous one. |
| **Bind node** | `bind-node`. Lock the session to one node. Rotation is disabled; if the node fails, the request fails with `503 BOUND_NODE_FAILED` unless paired with `onerror-retry_N`. |
`bind-node` requires a session and is incompatible with `rotate-timed_N`, `rotate-requests_N`, `onerror-replace`, and `prefer-lookalike`.
## Copy and test
When your settings produce the connection string you want, click **Copy** to grab it. The format is:
```
{rules}:{package_key}@proxy.soax.com:1337
```
Test it with one request:
```bash theme={null}
curl -x proxy.soax.com:1337 -U "country-us-session-job1:pk_abc123" https://checker.soax.com/api/ipinfo
```
A successful response includes a JSON body with the exit IP and its geolocation. If you targeted a country, the `country_code` should match.
## Worked examples
**Rotating US residential, no session:**
```
country-us:pk_abc123@proxy.soax.com:1337
```
**Session in the UK, rotate IP every 5 minutes:**
```
country-gb-session-uk1-rotate-timed_300:pk_abc123@proxy.soax.com:1337
```
**Mobile in New York, retry 3 times on error, prefer a similar replacement:**
```
network-mob-country-us-city-new_york-session-m1-onerror-retry_3-prefer-lookalike:pk_abc123@proxy.soax.com:1337
```
**Lock to a single node:**
```
country-us-session-locked1-bind-node:pk_abc123@proxy.soax.com:1337
```
## Next steps
Username/password and IP Auth, end-to-end.
Full rule reference for residential configuration.
Full rule reference for mobile configuration.
What each SOAX-level error means and how to fix it.
# Usage Analytics
Source: https://developers.soax.com/dashboard/usage-analytics
Monitor your proxy usage and traffic patterns and spot issues before they become problems.
The Usage Analytics dashboard gives you a detailed breakdown of how your proxies are being used. You can find it by clicking the bar chart icon in the left sidebar, or going to [Usage breakdown](https://platform.soax.com/usage).
## Filtering by date range
Use the date picker at the top of the page to change the time window. The summary cards and chart both update to reflect whichever range you select. This is useful for comparing a specific week or day against your baseline.
## The summary cards
At the top of the page, three cards give you a snapshot of activity for the selected time range.
### Total credits
The total number of credits consumed in the selected period. Credits are deducted based on the volume of traffic you route through SOAX and the country tier you target. If this number is higher than expected, the usage log below will help you narrow down which package or country is driving it.
### Total traffic
The total data transferred through your proxies, shown in GB. This is the raw volume your credits are spent on. A spike here is worth investigating in the chart.
### Credits/GB avg
Your average credits spent per GB in the selected period.
This reflects the blend of country tiers you've been targeting. A higher number means more traffic through Tier 1 countries (US, UK, Western Europe), which cost more per GB. A lower number means more Tier 2 or Tier 3 traffic. If it's trending up, check the **Country** and **Tier** filters in the usage insights to see what's shifted.
## Usage insights
The chart section lets you slice your traffic data to understand exactly where consumption is coming from.
### Filters
You can filter the chart and table by:
* **Proxy package** — isolate traffic from a specific package if you're running multiple
* **Proxy network** — filter by network type (e.g. residential)
* **Tier** — see how much of your traffic is going through Tier 1, 2, or 3 countries
* **Country** — drill down to a specific country
* **Interval** — switch between daily, weekly, or hourly granularity
Filters stack, so you can combine them — for example, residential traffic in Tier 1 countries over the last 7 days.
### Reading the chart
The **Usage over time** chart shows traffic volume in GB or consumed credits (toggle with the **Gb / Credits** buttons in the top right) broken down by your chosen grouping. By default it groups by package, with each package shown as a separate colour in the stacked columns.
Switch between **Columns** and **Lines** views depending on what you're trying to see:
* **Columns** make it easy to compare total volume across days
* **Lines** make it easier to see trends and rate of change over time
Use **Group by** to pivot the view — grouping by **Country** or **Tier** is useful when you want to understand your geographic cost distribution.
A sudden spike on a single day usually means a job ran with misconfigured session settings, or a retry loop sent far more requests than intended. Cross-reference the date in the usage log to find the package and egress IP involved.
## Usage log
Below the chart, the usage log gives you a row-by-row record of traffic activity. Each row represents an aggregated record for a specific combination of package, network, tier, country, egress IP, and domain as well as month / date / hour.
The columns are:
| Column | What it tells you |
| ------------- | --------------------------------------------------------------- |
| **Date** | When the traffic occurred |
| **Package** | Which proxy package was used (shown as a UUID) |
| **Network** | The proxy network type (e.g. residential) |
| **Tier** | The country tier (1, 2, or 3) — this determines the per-GB rate |
| **Country** | The country code of the exit node used |
| **Egress IP** | The specific proxy IP the traffic exited through |
| **Domain** | The target domain that was requested |
| **Traffic** | Data transferred for that record, in GB |
### Exporting data
Click **Export CSV** in the top right of the usage log to download the full log for the current filter and date range. This is useful for reconciling usage against your own application logs, or sharing consumption data with your team.
Use **All columns selected** to choose which columns to include in the export before downloading.
## How to spot issues
A few patterns worth knowing:
**Unexpectedly high traffic on a single day** — check the usage log filtered to that date. Look for a high-traffic egress IP or a domain you don't recognise. This often points to a misconfigured retry loop or a job that ran longer than expected.
**Credits/GB avg is higher than your plan rate** — this means you're routing traffic through more expensive country tiers than you intended. Filter the chart by **Tier** to confirm, then check whether your proxy configuration is targeting the right countries.
**Traffic spread across many egress IPs to the same domain** — this is normal for rotating proxy sessions. If you're seeing it on a session-based package, it may indicate your session duration is too short and connections are cycling more often than expected.
**No traffic showing for a package** — if a package shows zero usage when you expect some, check that the package is active in **Package Management** and that your credentials are correct. Authentication failures don't generate usage records.
# Advanced: Multi-Geo Monitoring in Go
Source: https://developers.soax.com/examples/advanced/multi-geo-go
Production-grade Go example that monitors a URL across multiple countries in parallel using SOAX residential proxies with a worker pool.
This example demonstrates a real-world pattern: checking the same URL from multiple countries in parallel, collecting structured results, and handling failures gracefully. It's designed as a starting point for price monitoring, ad verification, or geo-targeted data collection.
The program uses a worker pool where each worker gets its own session and country assignment. Workers retry on failure with exponential backoff, and the entire job can be cancelled with a context timeout.
## What this example covers
* Configurable worker pool with controlled concurrency
* One session per country (consistent IP per geo)
* Structured result collection across all workers
* Exponential backoff retry on failure
* Context-based timeout and graceful shutdown
* Clean separation between configuration, execution, and output
## Full code
```go theme={null}
package main
import (
"context"
"encoding/json"
"fmt"
"io"
"math"
"net/http"
"net/url"
"os"
"sync"
"time"
)
// Config holds the job configuration.
type Config struct {
PackageKey string
TargetURL string
Countries []string
MaxConcurrency int
MaxRetries int
Timeout time.Duration
}
// ProxyResult holds the outcome of a single geo-targeted request.
type ProxyResult struct {
Country string `json:"country"`
IP string `json:"ip,omitempty"`
ISP string `json:"isp,omitempty"`
City string `json:"city,omitempty"`
StatusCode int `json:"status_code"`
BodySize int `json:"body_size"`
Duration time.Duration `json:"duration"`
Attempts int `json:"attempts"`
Error string `json:"error,omitempty"`
}
// CheckerResponse matches the SOAX checker JSON structure.
type CheckerResponse struct {
Status bool `json:"status"`
Data struct {
IP string `json:"ip"`
CountryCode string `json:"country_code"`
CountryName string `json:"country_name"`
Region string `json:"region"`
City string `json:"city"`
ISP string `json:"isp"`
Carrier string `json:"carrier"`
} `json:"data"`
}
func main() {
cfg := Config{
PackageKey: os.Getenv("SOAX_PACKAGE_KEY"),
TargetURL: "https://checker.soax.com/api/ipinfo",
Countries: []string{
"us", "gb", "de", "fr", "jp",
"br", "au", "ca", "in", "kr",
},
MaxConcurrency: 5,
MaxRetries: 3,
Timeout: 30 * time.Second,
}
if cfg.PackageKey == "" {
fmt.Println("Set SOAX_PACKAGE_KEY environment variable")
os.Exit(1)
}
ctx, cancel := context.WithTimeout(context.Background(), cfg.Timeout)
defer cancel()
results := runJob(ctx, cfg)
output, _ := json.MarshalIndent(results, "", " ")
fmt.Println(string(output))
// Summary
succeeded := 0
for _, r := range results {
if r.Error == "" {
succeeded++
}
}
fmt.Printf("\n%d/%d countries succeeded\n", succeeded, len(results))
}
// runJob dispatches workers and collects results.
func runJob(ctx context.Context, cfg Config) []ProxyResult {
jobs := make(chan string, len(cfg.Countries))
results := make(chan ProxyResult, len(cfg.Countries))
// Start worker pool
var wg sync.WaitGroup
for i := 0; i < cfg.MaxConcurrency; i++ {
wg.Add(1)
go func(workerID int) {
defer wg.Done()
worker(ctx, workerID, cfg, jobs, results)
}(i)
}
// Send jobs
for _, country := range cfg.Countries {
jobs <- country
}
close(jobs)
// Wait for all workers to finish, then close results
go func() {
wg.Wait()
close(results)
}()
// Collect results
var collected []ProxyResult
for r := range results {
collected = append(collected, r)
}
return collected
}
// worker pulls countries from the jobs channel and fetches each one.
func worker(ctx context.Context, id int, cfg Config, jobs <-chan string, results chan<- ProxyResult) {
for country := range jobs {
select {
case <-ctx.Done():
results <- ProxyResult{
Country: country,
Error: "cancelled: timeout exceeded",
}
return
default:
result := fetchWithRetry(ctx, cfg, country)
results <- result
}
}
}
// fetchWithRetry attempts the request up to MaxRetries with exponential backoff.
func fetchWithRetry(ctx context.Context, cfg Config, country string) ProxyResult {
var lastErr error
for attempt := 1; attempt <= cfg.MaxRetries; attempt++ {
result, err := fetchCountry(ctx, cfg, country)
if err == nil {
result.Attempts = attempt
return result
}
lastErr = err
// Don't sleep after the last attempt
if attempt < cfg.MaxRetries {
backoff := time.Duration(math.Pow(2, float64(attempt-1))) * 500 * time.Millisecond
select {
case <-ctx.Done():
return ProxyResult{
Country: country,
Attempts: attempt,
Error: "cancelled: timeout exceeded",
}
case <-time.After(backoff):
// continue to next attempt
}
}
}
return ProxyResult{
Country: country,
Attempts: cfg.MaxRetries,
Error: lastErr.Error(),
}
}
// fetchCountry makes a single proxied request for a given country.
func fetchCountry(ctx context.Context, cfg Config, country string) (ProxyResult, error) {
sessionID := fmt.Sprintf("geo_%s", country)
proxyStr := fmt.Sprintf("http://country-%s-session-%s:%s@proxy.soax.com:1337", country, sessionID, cfg.PackageKey)
proxyURL, err := url.Parse(proxyStr)
if err != nil {
return ProxyResult{}, fmt.Errorf("invalid proxy URL: %w", err)
}
client := &http.Client{
Transport: &http.Transport{Proxy: http.ProxyURL(proxyURL)},
Timeout: 10 * time.Second,
}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, cfg.TargetURL, nil)
if err != nil {
return ProxyResult{}, fmt.Errorf("request creation failed: %w", err)
}
start := time.Now()
resp, err := client.Do(req)
duration := time.Since(start)
if err != nil {
return ProxyResult{}, fmt.Errorf("request failed: %w", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return ProxyResult{}, fmt.Errorf("body read failed: %w", err)
}
result := ProxyResult{
Country: country,
StatusCode: resp.StatusCode,
BodySize: len(body),
Duration: duration,
}
// Parse checker response to extract IP metadata
var checker CheckerResponse
if json.Unmarshal(body, &checker) == nil && checker.Status {
result.IP = checker.Data.IP
result.ISP = checker.Data.ISP
result.City = checker.Data.City
}
return result, nil
}
```
## How to run it
Set your package key as an environment variable:
```bash theme={null}
export SOAX_PACKAGE_KEY="abc123"
go run main.go
```
## Example output
```json theme={null}
[
{
"country": "us",
"ip": "104.28.55.12",
"isp": "Comcast",
"city": "Denver",
"status_code": 200,
"body_size": 203,
"duration": 842000000,
"attempts": 1
},
{
"country": "gb",
"ip": "86.12.145.78",
"isp": "BT",
"city": "Manchester",
"status_code": 200,
"body_size": 198,
"duration": 1103000000,
"attempts": 1
},
{
"country": "de",
"ip": "91.64.33.210",
"isp": "Deutsche Telekom",
"city": "Berlin",
"status_code": 200,
"body_size": 201,
"duration": 967000000,
"attempts": 1
}
]
10/10 countries succeeded
```
## How it works
**Worker pool.** The `MaxConcurrency` setting controls how many countries are checked at the same time. With 10 countries and 5 workers, the job runs in two waves. Increase concurrency to run all countries simultaneously, or decrease it to stay within your package's connection limits.
**One session per country.** Each country gets a session ID (`geo_us`, `geo_gb`, etc.). This means the same IP is reused if you run the job multiple times within the session's 60-second inactivity window. For monitoring jobs that run on a schedule, this gives you consistent IPs across runs.
**Exponential backoff.** If a request fails, the worker waits before retrying: 500ms after the first failure, 1s after the second, 2s after the third. This avoids hammering the proxy during transient issues.
**Context timeout.** The entire job has a 30-second deadline. If any worker is still running when the timeout hits, it gets cancelled and returns a clear error. This prevents hung requests from blocking your pipeline.
**Structured results.** Every result includes the country, exit IP, ISP, response timing, retry count, and any errors. This makes it easy to pipe into a database, alerting system, or dashboard.
## Adapting this for your use case
**Change the target URL.** Replace `checker.soax.com/api/ipinfo` with the URL you're monitoring. The result struct will need updating to match your target's response format.
**Add more countries.** The `Countries` slice controls which geos are checked. Add or remove country codes as needed.
**Switch to mobile.** Change the proxy string to include `network-mob`:
```go theme={null}
proxyStr := fmt.Sprintf("http://network-mob-country-%s-session-%s:%s@proxy.soax.com:1337", country, sessionID, cfg.PackageKey)
```
**Add rotation.** If you want a fresh IP on every run instead of reusing sessions, remove the `session` parameter from the proxy string, or append a timestamp to the session ID to force a new session each time:
```go theme={null}
sessionID := fmt.Sprintf("geo_%s_%d", country, time.Now().Unix())
```
**Write results to a file.** Replace the `fmt.Println` at the end with a file write for scheduled jobs:
```go theme={null}
os.WriteFile("results.json", output, 0644)
```
## Next steps
Full parameter reference for session, rotation, and error handling options.
Simpler Go examples for getting started.
Understand what errors mean and how to handle them in your code.
Troubleshoot timeouts, auth failures, and geo mismatches.
# curl
Source: https://developers.soax.com/examples/curl
Working curl commands for SOAX residential and mobile proxies. Covers rotating and sessions with copy-paste examples.
curl is the quickest way to test SOAX from your terminal. Every example on this page is a single command you can copy, paste, and run immediately.
Replace `YOUR_PACKAGE_KEY` with your actual package key from the [dashboard](https://platform.soax.com).
## Residential proxies
### Rotating (new IP every request)
```bash theme={null}
curl -x proxy.soax.com:1337 -U "country-us:YOUR_PACKAGE_KEY" https://checker.soax.com/api/ipinfo
```
Run this multiple times and you'll get a different IP each time.
### Target a specific city
```bash theme={null}
curl -x proxy.soax.com:1337 -U "country-us-city-new_york:YOUR_PACKAGE_KEY" https://checker.soax.com/api/ipinfo
```
### Session (same IP across requests)
```bash theme={null}
curl -x proxy.soax.com:1337 -U "country-us-session-mysession1:YOUR_PACKAGE_KEY" https://checker.soax.com/api/ipinfo
```
Run this multiple times with the same session ID and you'll see the same IP, as long as the session is active. Ephemeral sessions expire after 60 seconds of inactivity.
### Session with timed rotation
Rotate to a new IP every 5 minutes:
```bash theme={null}
curl -x proxy.soax.com:1337 -U "country-us-session-job1-rotate-timed_300:YOUR_PACKAGE_KEY" https://checker.soax.com/api/ipinfo
```
### Session with request-based rotation
Rotate to a new IP every 10 requests:
```bash theme={null}
curl -x proxy.soax.com:1337 -U "country-us-session-job1-rotate-requests_10:YOUR_PACKAGE_KEY" https://checker.soax.com/api/ipinfo
```
### Session with error handling
Retry 3 times on the same node before replacing:
```bash theme={null}
curl -x proxy.soax.com:1337 -U "country-us-session-job1-onerror-retry_3:YOUR_PACKAGE_KEY" https://checker.soax.com/api/ipinfo
```
Replace with a similar node on error:
```bash theme={null}
curl -x proxy.soax.com:1337 -U "country-us-session-job1-onerror-replace-prefer-lookalike:YOUR_PACKAGE_KEY" https://checker.soax.com/api/ipinfo
```
Fail immediately on error (no retry, no replacement):
```bash theme={null}
curl -x proxy.soax.com:1337 -U "country-us-session-job1-onerror-fail:YOUR_PACKAGE_KEY" https://checker.soax.com/api/ipinfo
```
## Mobile proxies
Mobile proxies use the same format. Add `network-mob` to your parameters.
### Rotating
```bash theme={null}
curl -x proxy.soax.com:1337 -U "network-mob-country-us:YOUR_PACKAGE_KEY" https://checker.soax.com/api/ipinfo
```
### Session
```bash theme={null}
curl -x proxy.soax.com:1337 -U "network-mob-country-us-session-mobile1:YOUR_PACKAGE_KEY" https://checker.soax.com/api/ipinfo
```
### Target a specific carrier
```bash theme={null}
curl -x proxy.soax.com:1337 -U "network-mob-country-us-isp-verizon_wireless-session-vz1:YOUR_PACKAGE_KEY" https://checker.soax.com/api/ipinfo
```
### Mobile session with timed rotation
```bash theme={null}
curl -x proxy.soax.com:1337 -U "network-mob-country-us-session-m1-rotate-timed_600:YOUR_PACKAGE_KEY" https://checker.soax.com/api/ipinfo
```
## IP Auth (HTTPS)
If you've added your IP to the allowlist in the dashboard, you can connect without credentials. Rules go in the subdomain instead.
```bash theme={null}
curl --proxy "https://country-us.proxy.soax.com:1337" https://checker.soax.com/api/ipinfo
```
With a session:
```bash theme={null}
curl --proxy "https://country-us-session-job1.proxy.soax.com:1337" https://checker.soax.com/api/ipinfo
```
IP Auth via subdomain is limited to 63 characters (the DNS label limit). For complex rule combinations, use the [parameter shortcuts](/proxies/residential#parameter-shortcuts) (e.g. `c-us-ci-new_york-s-job1`) or switch to username/password authentication.
## SOCKS5
To connect via SOCKS5 instead of HTTP, use `--socks5`:
```bash theme={null}
curl --socks5 proxy.soax.com:1337 --proxy-user "country-us:YOUR_PACKAGE_KEY" https://checker.soax.com/api/ipinfo
```
## Expected response
All examples above return a JSON response from the SOAX checker:
```json theme={null}
{
"status": true,
"data": {
"ip": "185.123.45.67",
"country_code": "US",
"country_name": "United States",
"region": "California",
"city": "Los Angeles",
"isp": "Spectrum",
"carrier": ""
}
}
```
The `ip` should be different from your real IP. If you targeted a specific country, the `country_code` should match.
## Next steps
Working Python code for SOAX proxies.
Working Node.js code for SOAX proxies.
What to do when something goes wrong.
Full parameter reference.
# Go
Source: https://developers.soax.com/examples/go
Working Go code for SOAX residential and mobile proxies. Copy-paste examples using net/http.
These examples use Go's standard `net/http` and `net/url` packages. No external dependencies needed. Replace `YOUR_PACKAGE_KEY` with your actual package key from the [dashboard](https://platform.soax.com).
## Residential proxies
### Rotating (new IP every request)
```go theme={null}
package main
import (
"fmt"
"io"
"net/http"
"net/url"
)
func main() {
proxyURL, _ := url.Parse("http://country-us:YOUR_PACKAGE_KEY@proxy.soax.com:1337")
client := &http.Client{
Transport: &http.Transport{Proxy: http.ProxyURL(proxyURL)},
}
resp, err := client.Get("https://checker.soax.com/api/ipinfo")
if err != nil {
fmt.Println("Error:", err)
return
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
```
Each time you run this, you'll get a different IP.
### Session (same IP across requests)
```go theme={null}
package main
import (
"fmt"
"io"
"net/http"
"net/url"
)
func main() {
proxyURL, _ := url.Parse("http://country-us-session-go1:YOUR_PACKAGE_KEY@proxy.soax.com:1337")
client := &http.Client{
Transport: &http.Transport{Proxy: http.ProxyURL(proxyURL)},
}
for i := 0; i < 3; i++ {
resp, err := client.Get("https://checker.soax.com/api/ipinfo")
if err != nil {
fmt.Println("Error:", err)
continue
}
body, _ := io.ReadAll(resp.Body)
resp.Body.Close()
fmt.Printf("Request %d: %s\n", i+1, string(body))
}
}
```
All three requests will return the same IP.
### Session with timed rotation
Rotate to a new IP every 5 minutes:
```go theme={null}
package main
import (
"fmt"
"io"
"net/http"
"net/url"
)
func main() {
proxyURL, _ := url.Parse("http://country-us-session-job1-rotate-timed_300:YOUR_PACKAGE_KEY@proxy.soax.com:1337")
client := &http.Client{
Transport: &http.Transport{Proxy: http.ProxyURL(proxyURL)},
}
resp, err := client.Get("https://checker.soax.com/api/ipinfo")
if err != nil {
fmt.Println("Error:", err)
return
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
```
### Target a specific city and ISP
```go theme={null}
package main
import (
"fmt"
"io"
"net/http"
"net/url"
)
func main() {
proxyURL, _ := url.Parse("http://country-us-city-los_angeles-isp-comcast:YOUR_PACKAGE_KEY@proxy.soax.com:1337")
client := &http.Client{
Transport: &http.Transport{Proxy: http.ProxyURL(proxyURL)},
}
resp, err := client.Get("https://checker.soax.com/api/ipinfo")
if err != nil {
fmt.Println("Error:", err)
return
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
```
### Session with error retry and lookalike replacement
```go theme={null}
package main
import (
"fmt"
"io"
"net/http"
"net/url"
)
func main() {
proxyURL, _ := url.Parse("http://country-us-session-scrape1-onerror-retry_3-prefer-lookalike:YOUR_PACKAGE_KEY@proxy.soax.com:1337")
client := &http.Client{
Transport: &http.Transport{Proxy: http.ProxyURL(proxyURL)},
}
resp, err := client.Get("https://checker.soax.com/api/ipinfo")
if err != nil {
fmt.Println("Error:", err)
return
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
```
## Mobile proxies
Add `network-mob` to the proxy URL. Everything else works the same way.
### Rotating
```go theme={null}
package main
import (
"fmt"
"io"
"net/http"
"net/url"
)
func main() {
proxyURL, _ := url.Parse("http://network-mob-country-us:YOUR_PACKAGE_KEY@proxy.soax.com:1337")
client := &http.Client{
Transport: &http.Transport{Proxy: http.ProxyURL(proxyURL)},
}
resp, err := client.Get("https://checker.soax.com/api/ipinfo")
if err != nil {
fmt.Println("Error:", err)
return
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
```
### Session
```go theme={null}
package main
import (
"fmt"
"io"
"net/http"
"net/url"
)
func main() {
proxyURL, _ := url.Parse("http://network-mob-country-us-session-mobile1:YOUR_PACKAGE_KEY@proxy.soax.com:1337")
client := &http.Client{
Transport: &http.Transport{Proxy: http.ProxyURL(proxyURL)},
}
for i := 0; i < 3; i++ {
resp, err := client.Get("https://checker.soax.com/api/ipinfo")
if err != nil {
fmt.Println("Error:", err)
continue
}
body, _ := io.ReadAll(resp.Body)
resp.Body.Close()
fmt.Printf("Request %d: %s\n", i+1, string(body))
}
}
```
### Target a specific carrier
```go theme={null}
package main
import (
"fmt"
"io"
"net/http"
"net/url"
)
func main() {
proxyURL, _ := url.Parse("http://network-mob-country-us-isp-verizon_wireless-session-vz1:YOUR_PACKAGE_KEY@proxy.soax.com:1337")
client := &http.Client{
Transport: &http.Transport{Proxy: http.ProxyURL(proxyURL)},
}
resp, err := client.Get("https://checker.soax.com/api/ipinfo")
if err != nil {
fmt.Println("Error:", err)
return
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
```
## Multiple concurrent sessions
Run several sessions in parallel using goroutines:
```go theme={null}
package main
import (
"fmt"
"io"
"net/http"
"net/url"
"sync"
)
type target struct {
session string
country string
}
func main() {
targets := []target{
{session: "target_a", country: "us"},
{session: "target_b", country: "gb"},
{session: "target_c", country: "de"},
}
var wg sync.WaitGroup
for _, t := range targets {
wg.Add(1)
go func(t target) {
defer wg.Done()
proxyStr := fmt.Sprintf("http://country-%s-session-%s:YOUR_PACKAGE_KEY@proxy.soax.com:1337", t.country, t.session)
proxyURL, _ := url.Parse(proxyStr)
client := &http.Client{
Transport: &http.Transport{Proxy: http.ProxyURL(proxyURL)},
}
resp, err := client.Get("https://checker.soax.com/api/ipinfo")
if err != nil {
fmt.Printf("%s: error: %s\n", t.session, err)
return
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Printf("%s: %s\n", t.session, string(body))
}(t)
}
wg.Wait()
}
```
## Expected response
All examples return a JSON response from the SOAX checker:
```json theme={null}
{
"status": true,
"data": {
"ip": "185.123.45.67",
"country_code": "US",
"country_name": "United States",
"region": "California",
"city": "Los Angeles",
"isp": "Spectrum",
"carrier": ""
}
}
```
## Next steps
Working Python code for SOAX proxies.
Working Node.js code for SOAX proxies.
Full parameter reference.
What to do when something goes wrong.
# Node.js
Source: https://developers.soax.com/examples/nodejs
Working Node.js code for SOAX residential and mobile proxies. Copy-paste examples using axios and node-fetch.
These examples use [axios](https://axios-http.com/) with [https-proxy-agent](https://www.npmjs.com/package/https-proxy-agent). Replace `YOUR_PACKAGE_KEY` with your actual package key from the [dashboard](https://platform.soax.com).
Install the dependencies:
```bash theme={null}
npm install axios https-proxy-agent
```
## Residential proxies
### Rotating (new IP every request)
```javascript theme={null}
const axios = require("axios");
const { HttpsProxyAgent } = require("https-proxy-agent");
const agent = new HttpsProxyAgent("http://country-us:YOUR_PACKAGE_KEY@proxy.soax.com:1337");
axios.get("https://checker.soax.com/api/ipinfo", { httpsAgent: agent })
.then(res => console.log(res.data))
.catch(err => console.error(err.message));
```
Each time you run this, you'll get a different IP.
### Session (same IP across requests)
```javascript theme={null}
const axios = require("axios");
const { HttpsProxyAgent } = require("https-proxy-agent");
const agent = new HttpsProxyAgent("http://country-us-session-node1:YOUR_PACKAGE_KEY@proxy.soax.com:1337");
async function run() {
for (let i = 0; i < 3; i++) {
const res = await axios.get("https://checker.soax.com/api/ipinfo", { httpsAgent: agent });
const data = res.data.data;
console.log(`Request ${i + 1}: ${data.ip} (${data.city}, ${data.country_code})`);
}
}
run();
```
All three requests will return the same IP.
### Session with timed rotation
Rotate to a new IP every 5 minutes:
```javascript theme={null}
const axios = require("axios");
const { HttpsProxyAgent } = require("https-proxy-agent");
const agent = new HttpsProxyAgent("http://country-us-session-job1-rotate-timed_300:YOUR_PACKAGE_KEY@proxy.soax.com:1337");
axios.get("https://checker.soax.com/api/ipinfo", { httpsAgent: agent })
.then(res => console.log(res.data))
.catch(err => console.error(err.message));
```
### Target a specific city and ISP
```javascript theme={null}
const axios = require("axios");
const { HttpsProxyAgent } = require("https-proxy-agent");
const agent = new HttpsProxyAgent("http://country-us-city-new_york-isp-comcast:YOUR_PACKAGE_KEY@proxy.soax.com:1337");
axios.get("https://checker.soax.com/api/ipinfo", { httpsAgent: agent })
.then(res => console.log(res.data))
.catch(err => console.error(err.message));
```
### Session with error retry and lookalike replacement
```javascript theme={null}
const axios = require("axios");
const { HttpsProxyAgent } = require("https-proxy-agent");
const agent = new HttpsProxyAgent("http://country-us-session-scrape1-onerror-retry_3-prefer-lookalike:YOUR_PACKAGE_KEY@proxy.soax.com:1337");
axios.get("https://checker.soax.com/api/ipinfo", { httpsAgent: agent })
.then(res => console.log(res.data))
.catch(err => console.error(err.message));
```
## Mobile proxies
Add `network-mob` to the proxy URL. Everything else works the same way.
### Rotating
```javascript theme={null}
const axios = require("axios");
const { HttpsProxyAgent } = require("https-proxy-agent");
const agent = new HttpsProxyAgent("http://network-mob-country-us:YOUR_PACKAGE_KEY@proxy.soax.com:1337");
axios.get("https://checker.soax.com/api/ipinfo", { httpsAgent: agent })
.then(res => console.log(res.data))
.catch(err => console.error(err.message));
```
### Session
```javascript theme={null}
const axios = require("axios");
const { HttpsProxyAgent } = require("https-proxy-agent");
const agent = new HttpsProxyAgent("http://network-mob-country-us-session-mobile1:YOUR_PACKAGE_KEY@proxy.soax.com:1337");
async function run() {
for (let i = 0; i < 3; i++) {
const res = await axios.get("https://checker.soax.com/api/ipinfo", { httpsAgent: agent });
const data = res.data.data;
console.log(`Request ${i + 1}: ${data.ip} (${data.carrier})`);
}
}
run();
```
### Target a specific carrier
```javascript theme={null}
const axios = require("axios");
const { HttpsProxyAgent } = require("https-proxy-agent");
const agent = new HttpsProxyAgent("http://network-mob-country-us-isp-t_mobile-session-tm1:YOUR_PACKAGE_KEY@proxy.soax.com:1337");
axios.get("https://checker.soax.com/api/ipinfo", { httpsAgent: agent })
.then(res => console.log(res.data))
.catch(err => console.error(err.message));
```
## Multiple concurrent sessions
Run several sessions in parallel with unique session IDs:
```javascript theme={null}
const axios = require("axios");
const { HttpsProxyAgent } = require("https-proxy-agent");
const targets = [
{ session: "target_a", country: "us" },
{ session: "target_b", country: "gb" },
{ session: "target_c", country: "de" },
];
async function fetch(target) {
const agent = new HttpsProxyAgent(
`http://country-${target.country}-session-${target.session}:YOUR_PACKAGE_KEY@proxy.soax.com:1337`
);
const res = await axios.get("https://checker.soax.com/api/ipinfo", { httpsAgent: agent });
const data = res.data.data;
return `${target.session}: ${data.ip} (${data.country_code})`;
}
Promise.all(targets.map(fetch)).then(results => results.forEach(r => console.log(r)));
```
## Using node-fetch
If you prefer node-fetch over axios:
```bash theme={null}
npm install node-fetch https-proxy-agent
```
```javascript theme={null}
const fetch = require("node-fetch");
const { HttpsProxyAgent } = require("https-proxy-agent");
const agent = new HttpsProxyAgent("http://country-us:YOUR_PACKAGE_KEY@proxy.soax.com:1337");
fetch("https://checker.soax.com/api/ipinfo", { agent })
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.error(err.message));
```
## Expected response
All examples return a JSON response from the SOAX checker:
```json theme={null}
{
"status": true,
"data": {
"ip": "185.123.45.67",
"country_code": "US",
"country_name": "United States",
"region": "California",
"city": "Los Angeles",
"isp": "Spectrum",
"carrier": ""
}
}
```
## Next steps
Working Python code for SOAX proxies.
Working Go code for SOAX proxies.
Full parameter reference.
What to do when something goes wrong.
# Python
Source: https://developers.soax.com/examples/python
Working Python code for SOAX residential and mobile proxies. Copy-paste examples using the requests library.
These examples use the [requests](https://docs.python-requests.org/) library. Replace `YOUR_PACKAGE_KEY` with your actual package key from the [dashboard](https://platform.soax.com).
Install requests if you haven't already:
```bash theme={null}
pip install requests
```
## Residential proxies
### Rotating (new IP every request)
```python theme={null}
import requests
proxy_url = "http://country-us:YOUR_PACKAGE_KEY@proxy.soax.com:1337"
response = requests.get(
"https://checker.soax.com/api/ipinfo",
proxies={"http": proxy_url, "https": proxy_url},
)
print(response.json())
```
Each time you run this, you'll get a different IP.
### Session (same IP across requests)
```python theme={null}
import requests
proxy_url = "http://country-us-session-python1:YOUR_PACKAGE_KEY@proxy.soax.com:1337"
for i in range(3):
response = requests.get(
"https://checker.soax.com/api/ipinfo",
proxies={"http": proxy_url, "https": proxy_url},
)
data = response.json()["data"]
print(f"Request {i + 1}: {data['ip']} ({data['city']}, {data['country_code']})")
```
All three requests will return the same IP.
### Session with timed rotation
Rotate to a new IP every 5 minutes:
```python theme={null}
import requests
proxy_url = "http://country-us-session-job1-rotate-timed_300:YOUR_PACKAGE_KEY@proxy.soax.com:1337"
response = requests.get(
"https://checker.soax.com/api/ipinfo",
proxies={"http": proxy_url, "https": proxy_url},
)
print(response.json())
```
### Target a specific city and ISP
```python theme={null}
import requests
proxy_url = "http://country-us-city-los_angeles-isp-comcast:YOUR_PACKAGE_KEY@proxy.soax.com:1337"
response = requests.get(
"https://checker.soax.com/api/ipinfo",
proxies={"http": proxy_url, "https": proxy_url},
)
print(response.json())
```
### Session with error retry and lookalike replacement
```python theme={null}
import requests
proxy_url = "http://country-us-session-scrape1-onerror-retry_3-prefer-lookalike:YOUR_PACKAGE_KEY@proxy.soax.com:1337"
response = requests.get(
"https://checker.soax.com/api/ipinfo",
proxies={"http": proxy_url, "https": proxy_url},
)
print(response.json())
```
## Mobile proxies
Add `network-mob` to the proxy URL. Everything else works the same way.
### Rotating
```python theme={null}
import requests
proxy_url = "http://network-mob-country-us:YOUR_PACKAGE_KEY@proxy.soax.com:1337"
response = requests.get(
"https://checker.soax.com/api/ipinfo",
proxies={"http": proxy_url, "https": proxy_url},
)
print(response.json())
```
### Session
```python theme={null}
import requests
proxy_url = "http://network-mob-country-us-session-mobile1:YOUR_PACKAGE_KEY@proxy.soax.com:1337"
for i in range(3):
response = requests.get(
"https://checker.soax.com/api/ipinfo",
proxies={"http": proxy_url, "https": proxy_url},
)
data = response.json()["data"]
print(f"Request {i + 1}: {data['ip']} ({data['carrier']})")
```
### Target a specific carrier
```python theme={null}
import requests
proxy_url = "http://network-mob-country-us-isp-t_mobile-session-tm1:YOUR_PACKAGE_KEY@proxy.soax.com:1337"
response = requests.get(
"https://checker.soax.com/api/ipinfo",
proxies={"http": proxy_url, "https": proxy_url},
)
print(response.json())
```
## Multiple concurrent sessions
If you need to run several sessions in parallel (for example, scraping different targets), give each one a unique session ID:
```python theme={null}
import requests
from concurrent.futures import ThreadPoolExecutor
targets = [
{"session": "target_a", "country": "us"},
{"session": "target_b", "country": "gb"},
{"session": "target_c", "country": "de"},
]
def fetch(target):
proxy_url = f"http://country-{target['country']}-session-{target['session']}:YOUR_PACKAGE_KEY@proxy.soax.com:1337"
response = requests.get(
"https://checker.soax.com/api/ipinfo",
proxies={"http": proxy_url, "https": proxy_url},
)
data = response.json()["data"]
return f"{target['session']}: {data['ip']} ({data['country_code']})"
with ThreadPoolExecutor(max_workers=3) as pool:
for result in pool.map(fetch, targets):
print(result)
```
## Expected response
All examples return a JSON response from the SOAX checker:
```json theme={null}
{
"status": true,
"data": {
"ip": "185.123.45.67",
"country_code": "US",
"country_name": "United States",
"region": "California",
"city": "Los Angeles",
"isp": "Spectrum",
"carrier": ""
}
}
```
## Next steps
Working Node.js code for SOAX proxies.
Working Go code for SOAX proxies.
Full parameter reference.
What to do when something goes wrong.
# Authentication
Source: https://developers.soax.com/getting-started/authentication
How to authenticate with SOAX proxies using username/password or IP Auth. Includes connection string format, where to find credentials, and working examples.
SOAX supports two ways to authenticate your proxy requests: **username/password** and **IP Auth**. Both are configured per proxy package in the dashboard.
## Where to find your credentials
Each proxy package has its own credentials. To find them:
1. Log in to the [dashboard](https://platform.soax.com).
2. Open **Quick Connect** or go to your proxy package settings.
3. Your **package key** (password) is displayed there. Copy it exactly as shown. Package keys look like `pk_abc123…`.
You can regenerate your package key at any time from the dashboard. This immediately invalidates the old key, so make sure to update it everywhere you use it before regenerating.
## Username/password authentication
This is the most common way to connect. You send your credentials with every request as part of the proxy connection string.
The format is:
```
{rules}:{package_key}@proxy.soax.com:1337
```
The **username** field carries your rules (country, session, network type, rotation, error handling). The **password** field is your package key. They are separated by a colon.
Here's a basic example that routes through a US residential proxy:
```bash theme={null}
curl -x proxy.soax.com:1337 -U "country-us:pk_abc123" https://checker.soax.com/api/ipinfo
```
You should get a response like this:
```json theme={null}
{
"status": true,
"data": {
"ip": "185.123.45.67",
"country_code": "US",
"country_name": "United States",
"region": "California",
"city": "Los Angeles",
"isp": "Spectrum",
"carrier": ""
}
}
```
You need at least one targeting rule in the username. If you want the full global pool without any geographic filter, use `country-any`:
```bash theme={null}
curl -x proxy.soax.com:1337 -U "country-any:pk_abc123" https://checker.soax.com/api/ipinfo
```
### Adding more rules
You can chain multiple rules in the username field using hyphens as delimiters. Multi-word values use underscores instead of hyphens (the hyphen is the rule delimiter).
Here are some examples:
**Target a specific country and city:**
```
country-us-city-new_york:pk_abc123@proxy.soax.com:1337
```
**Use mobile network in a specific region:**
```
network-mob-country-us-region-california:pk_abc123@proxy.soax.com:1337
```
**Add a session to keep the same IP across requests:**
```
country-us-session-job42:pk_abc123@proxy.soax.com:1337
```
Rules can appear in any order. The full list is covered in the [Residential proxies](/proxies/residential) and [Mobile proxies](/proxies/mobile) reference pages.
### Supported protocols
Username/password authentication works with all proxy protocols on the same gateway port:
| Protocol | Supported | Port |
| -------- | --------- | ---- |
| HTTP | Yes | 1337 |
| HTTPS | Yes | 1337 |
| SOCKS5 | Yes | 1337 |
## IP Auth
If you don't want to send credentials with every request, you can authenticate by IP instead. Once your client IP is on the package's allowlist, SOAX authenticates based on where the request comes from.
### Setting up IP Auth
1. In the dashboard, open your proxy package settings.
2. Find the **IP Allowlist** section.
3. Add the public IP address of the machine that will send requests.
Once added, you can connect without sending a username and password. With Quick Connect, the rules go in the subdomain instead.
### Quick Connect with IP Auth (HTTPS only)
```
https://{rules}.proxy.soax.com:1337
```
For example, to route through a US proxy:
```bash theme={null}
curl --proxy "https://country-us.proxy.soax.com:1337" https://checker.soax.com/api/ipinfo
```
This works at the start of the TLS handshake (SNI), which is why it's HTTPS-only — there is no equivalent field for SOAX to read with IP Auth on plain HTTP or SOCKS5.
Subdomains are subject to a 63-character DNS label limit (RFC 1035). If your rule string is long (combining country, city, ISP, session, and rotation rules), you may hit this limit. Use the [parameter shortcuts](/proxies/residential#parameter-shortcuts) (e.g. `c-us-ci-new_york-s-job1` instead of `country-us-city-new_york-session-job1`) to keep it short, or switch to username/password authentication, which has no rule-length limit.
### IP Auth over HTTP and SOCKS5
There is no way to pass runtime rules with IP Auth on plain HTTP or SOCKS5. HTTP has no username field and no SNI, and SOCKS5 has no username field under IP Auth. IP Auth carries rules over HTTPS only, via the subdomain (SNI), as shown above.
If you need runtime rules on HTTP or SOCKS5, use **username/password** authentication instead — the rules go in the username field, and all three protocols support it.
### Protocol support summary
| Auth method | HTTP | HTTPS | SOCKS5 |
| ---------------------------- | ---- | ----- | ------ |
| Username / password | Yes | Yes | Yes |
| IP Auth (rules in subdomain) | No | Yes | No |
## Which method should I use?
For most use cases, **username/password** is the simplest option. It works with all protocols, has no rule-length limits, and doesn't need any dashboard setup beyond copying your package key.
**IP Auth** is useful when your tool or integration doesn't support proxy credentials (some browser automation tools and legacy HTTP clients fall into this category), or when you want to avoid embedding credentials in configuration files.
You can use both methods on the same package. Adding an IP to the allowlist doesn't disable username/password authentication.
## Errors
If authentication fails, the proxy returns an error with two response headers: `X-SOAX-Error` (a machine-readable code) and `X-SOAX-Detail` (a plain explanation). The common ones:
| HTTP | X-SOAX-Error | Meaning |
| ---- | ------------------- | ------------------------------------- |
| 407 | `AUTH_FAILED` | Invalid or missing package key |
| 407 | `IP_NOT_ALLOWED` | Your client IP isn't on the allowlist |
| 407 | `PACKAGE_SUSPENDED` | Account suspended or disabled |
See [Error codes](/troubleshooting/error-codes) for the full list.
## Next steps
Full rule reference for residential proxy configuration.
Full rule reference for mobile proxy configuration.
Generate connection strings from the dashboard without memorizing the format.
What to do if you get a 407 AUTH\_FAILED or IP\_NOT\_ALLOWED error.
# Choosing the Right Proxy Type
Source: https://developers.soax.com/getting-started/choosing-proxy-type
Understand the difference between residential and mobile proxies, when to use each, and how to configure them on your package.
SOAX offers two network types for proxy traffic: **residential (WiFi)** and **mobile**. Both are included in every plan at the same credit rate, and both are enabled by default on new proxy packages.
You don't need to pick one or the other upfront. You can use both at the same time, or disable one on a per-package basis if you want to control which network type your team uses.
This page helps you understand when each network type works best.
## Residential (WiFi)
Residential proxies route your traffic through real home internet connections. The IPs belong to genuine ISP subscribers, so websites see them as regular household traffic.
SOAX's residential pool includes over 155 million IPs across 195+ countries.
**When to use residential proxies:**
* **Web scraping at scale.** Most anti-bot systems treat residential IPs as legitimate traffic. This gives you higher success rates on difficult targets like search engines, ecommerce platforms, and social media sites.
* **Price monitoring.** Residential IPs let you check pricing from specific countries and cities without triggering geo-based blocking or CAPTCHA walls.
* **Ad verification.** You can view ads as a real user would in any location, making it easier to detect fraud, misplacement, or geo-targeting issues.
* **SEO monitoring.** Track search rankings from different locations using IPs that search engines treat as genuine users.
* **General-purpose data collection.** If you're not sure which network type to use, start with residential. It covers the widest range of use cases.
## Mobile
Mobile proxies route your traffic through real mobile carrier connections on 3G, 4G, and 5G networks. The IPs come from carriers like Verizon, AT\&T, Deutsche Telekom, and others worldwide.
SOAX's mobile pool includes over 30 million IPs from carriers globally.
**When to use mobile proxies:**
* **Mobile-specific content.** Some websites and apps serve different content to mobile users. Mobile proxies let you see exactly what a mobile visitor would see.
* **App testing and verification.** If you're verifying how your app or mobile ads appear across carriers and regions, mobile IPs give you an authentic testing environment.
* **Highly protected targets.** Some websites that aggressively block residential IPs are more lenient with mobile traffic, because mobile carrier IPs are shared across many real users by design. This makes them harder to block without affecting legitimate visitors.
* **Social media platforms.** Mobile IPs are common for social media access, so they tend to blend in well on platforms that scrutinize connection types.
## How they compare
| | Residential (WiFi) | Mobile |
| ------------------ | ---------------------------------------------------- | ------------------------------------------------------ |
| IP source | Home ISP connections | Mobile carrier connections (3G/4G/5G) |
| Pool size | 155M+ IPs | 30M+ IPs |
| Best for | Web scraping, price monitoring, SEO, ad verification | Mobile content, app testing, heavily protected targets |
| Detection risk | Low | Very low |
| Credit cost | Same per tier | Same per tier |
| Geo-targeting | Country, region, city, ISP, ASN, zip | Country, region, city, carrier (via `isp`), ASN, zip |
| Enabled by default | Yes | Yes |
## You don't have to choose
Both network types share the same credit pool and burn at the same rate per geographic tier. There's no price difference between residential and mobile traffic in the same country.
When you create a proxy package, both are enabled by default. If you want to restrict a package to only residential or only mobile traffic, you can change this in the package settings. This is useful when you want to isolate use cases, for example giving your scraping team residential-only access and your mobile QA team mobile-only access.
For details on setting up and configuring packages, see [Package Management](/dashboard/package-management).
## Other network types
SOAX also offers datacenter and ISP proxies for specific use cases. These use different IP sources (data center infrastructure and static ISP-assigned IPs respectively) and are suited to tasks where speed or IP consistency matters more than appearing as a typical home or mobile user.
## Next steps
Set up your credentials and start connecting.
Full reference for residential proxy configuration and parameters.
Full reference for mobile proxy configuration and parameters.
Make your first request in under 5 minutes.
# Core Concepts
Source: https://developers.soax.com/getting-started/core-concepts
How SOAX's proxy system is built. Understand packages, sessions, rules, and bindings so you can get the most out of the platform.
Most proxy providers give you an IP and hope it works. SOAX works differently.
We built a system where you describe what you need (where, how, for how long, what happens when something breaks) and the platform handles the rest. You don't manage IPs directly. You set rules, and the system executes them predictably, even at high concurrency and at scale.
This page explains the building blocks that make this work: **packages**, **sessions**, **rules**, and **bindings**. Understanding these concepts will help you get more out of SOAX than just rotating through IPs.
## Packages
A **proxy package** is your entry point to the proxy network. It's where credentials, limits, and access controls live.
When you create a package in the dashboard, you're setting up a self-contained unit of proxy access. Each package has its own:
* **Package key** for authentication
* **Network types** (residential, mobile, or both)
* **Traffic limits** (optional, set by the organization owner)
* **RPS and connection limits** controlling throughput
* **IP allowlist** for IP-based authentication
* **Assigned users** who can use the package
Packages exist so you can separate and control how different teams or use cases consume proxy traffic. For example, you might create one package for your scraping pipeline with residential-only access and a 500GB limit, and another for your QA team with mobile enabled and no limit.
All usage is tracked per package, which means you can see exactly how much traffic each use case or team is generating. Credits are consumed from your organization's shared pool, but packages control who can spend them and how.
For more on managing packages, see [Package Management](/dashboard/package-management).
## Sessions
A **session** groups related requests and gives them a shared context.
Without a session, every request is independent. The system picks a new node each time, with no memory of what happened before. This is rotating mode, and it's the right choice for high-volume work where you don't need IP continuity.
But many workflows need continuity. You might need to log in, navigate to a page, and then scrape it. Or you might need to maintain the same IP for a few minutes while you collect data from a multi-page flow. That's what sessions are for.
When you create a session (by adding a `session` parameter to your connection string), the system binds your session to a node and keeps using that node for subsequent requests. All the rules you set apply consistently across every request in that session. Changes to the node only happen when your rules allow it.
```
country-us-session-job1:YOUR_PACKAGE_KEY@proxy.soax.com:1337
```
You pick the session ID (letters, digits, and underscores, up to 32 characters). The rules are set on the first request and apply for the lifetime of the session — if you send the same session ID later with different rules, the request is rejected with `409 SESSION_PARAMS_MISMATCH`. To change rules, use a new session ID. Sessions expire after 60 seconds of inactivity.
The key principle: sessions give you controlled continuity. You're not locked to a random IP and hoping it stays up. You're telling the system what behavior you need, and it maintains that behavior across requests.
## Rules
**Rules** are what set SOAX apart from most proxy providers.
Instead of giving you a few toggles (country, rotation time), SOAX uses a composable rules system with four independent categories. Each category answers a different question about how your requests should be handled:
### Filtering rules: which nodes can be used?
Filtering rules constrain the pool of eligible nodes based on their characteristics. You can filter by country, region, city, ISP, ASN, zip code, or network type.
These filters are enforced continuously. If you're in a session and the bound node stops matching your filters (for example, the node's IP is reclassified from US to JP), the binding becomes invalid and the system replaces it.
```
country-us-city-new_york-isp-comcast
```
### Routing rules: how does the system choose between eligible nodes?
Once the system knows which nodes are eligible, routing rules influence which one it picks. The available option is `prefer-lookalike`, which tells the system to favor nodes with similar characteristics to the previous one when it needs to select a replacement.
This matters when your node gets replaced (due to rotation, error, or reclassification). Without `prefer-lookalike`, you might jump from a Comcast IP in New York to a Verizon IP in Texas. With it, the system tries to find another Comcast IP in New York first.
```
prefer-lookalike
```
### Rotation rules: when is the system allowed to change the node?
Rotation rules control when the current node can be replaced during a session. You can rotate after a set number of seconds (`rotate-timed_N`, max 1 week, accepts `s`/`m`/`h` suffixes) or after a set number of requests (`rotate-requests_N`, max 1,000,000).
Without a rotation rule, a session keeps the same node until it encounters an error or its filters stop matching.
```
rotate-timed_300
rotate-requests_50
```
### Error handling rules: what happens when something goes wrong?
Error handling rules define how the system reacts to infrastructure failures (node goes offline, connection refused, timeout).
You have three options: replace the node with a new one (default), retry the request on the same node a set number of times before replacing (max 10), or fail immediately and return the error to your client.
```
onerror-replace
onerror-retry_3
onerror-fail
```
SOAX works at the transport layer, so the failures it can act on are infrastructure failures — node offline, connection refused, timeout. It can't see HTTP status codes or soft blocks inside an HTTPS tunnel, so target-side responses (403, 429, CAPTCHA pages) are your application's problem to detect and handle.
### Why this matters
These four categories are independent. You can combine any filtering rule with any routing preference, any rotation trigger, and any error handling strategy. They compose together in a single connection string:
```
country-us-city-new_york-session-job1-rotate-timed_300-onerror-retry_3-prefer-lookalike
```
That string says: use a US node in New York, maintain a session called "job1", rotate to a new node every 300 seconds, retry 3 times on the same node before replacing on error, and when you do replace, prefer a node with similar characteristics.
Most proxy providers make you choose between rotating and session-based connections and give you a country dropdown. SOAX lets you describe exact behavior for every scenario your requests might encounter. The system then executes that behavior deterministically, whether you're sending 10 requests or 10 million.
## Bindings
A **binding** is the link between a session and a node.
When your session's first request is processed, the system selects a node that matches your filtering rules and creates a binding. That node handles all subsequent requests in the session until something causes the binding to change.
A binding can change when:
* A rotation rule triggers (timed or request-count)
* The node fails and error handling replaces it
* The node no longer matches your filtering rules
When a binding changes, the system selects a new node using your filtering and routing rules, and creates a new binding. The session continues without interruption.
You normally don't interact with bindings directly. They're managed by the rules you set. But there's one exception: `bind-node`.
When you use `bind-node`, you're telling the system to lock the session to exactly one node. No rotation, no automatic replacement. If that node goes offline, your requests fail with an explicit error (`503 BOUND_NODE_FAILED`) instead of silently switching to a different node. This is useful for workflows where using a different node mid-session would cause problems (for example, maintaining a logged-in state on a target that fingerprints by IP).
`bind-node` requires a session and is incompatible with `rotate-timed_N`, `rotate-requests_N`, `onerror-replace`, and `prefer-lookalike`. You can pair it with `onerror-retry_N` to retry on the same node before failing.
## Nodes and IPs
A **node** is a real device in the SOAX network that executes your requests. It has a physical location, a network connection, and an IP address that the target website sees.
Nodes are not permanent. They're real devices on real networks, which means they can change IP, go offline, or shift characteristics over time. SOAX is designed to handle this as a normal operating condition, not as an exception.
This is why continuity in SOAX doesn't live at the IP level. It lives in sessions and bindings. Your session's rules define what behavior you need, and the system delivers it using whatever nodes are currently available and eligible. If a node changes or disappears, the rules handle what happens next.
This is a deliberate design choice. Systems that promise IP stability are making guarantees they can't always keep, especially at scale. SOAX instead gives you explicit control over what happens when things change, so your workflows stay predictable.
## How it all fits together
1. Your **organization** has a credit pool and one or more **packages**.
2. Each **package** defines who can connect, what network types are available, and what limits apply.
3. When you send a request, you include **rules** in your connection string that describe what you need.
4. If you include a **session** parameter, the system creates a session and binds it to a node that matches your rules.
5. The **binding** persists across requests, changing only when your rotation, error handling, or filtering rules require it.
6. If you don't include a session, each request is independent and gets a fresh node every time.
The result is a system where you describe behavior, not infrastructure. You don't need to know which specific node or IP you're using. You just define the rules, and the system handles the rest.
## Next steps
Make your first request in under 5 minutes.
Full parameter reference for residential proxy rules.
Full parameter reference for mobile proxy rules.
How to set up credentials and connect.
# Quickstart
Source: https://developers.soax.com/getting-started/quickstart
Go from signup to your first successful proxy request in under 5 minutes. Covers both the dashboard and terminal.
This guide walks you through making your first proxy request with SOAX. By the end, you'll have a working connection that returns a response through one of our residential proxy IPs.
## 1. Create your account
Go to [soax.com/signup](https://soax.com/signup) and create an account. You'll need to verify your email address before you can access the dashboard. Check your inbox (and spam folder) for the verification link.
## 2. Choose a package
Before you can make requests, you need an active package. After verifying your email, log in to the [dashboard](https://platform.soax.com) and select a package that fits your needs.
## 3. Make your first request
Once your package is active, you have two ways to make your first request: through the dashboard using **Quick Connect**, or from your **terminal** using curl.
### Option A: Quick Connect (dashboard)
Quick Connect is the fastest way to test your connection without writing any code.
In the dashboard sidebar, click [**Packages**](https://platform.soax.com/packages).
Select your proxy type (residential or mobile), pick a country, and choose whether to use a rotating connection or a session.
Quick Connect generates a connection string with your credentials. Copy it.
Use the built-in test button to send a request and confirm your proxy is working. You should see a response with an IP address that's different from your own.
For more details on Quick Connect options, see the [Quick Connect guide](/dashboard/quick-connect).
### Option B: curl (terminal)
If you prefer working from the command line, you can test your connection with a single curl command.
Replace `pk_abc123` with the package key from your dashboard:
```bash theme={null}
curl -x proxy.soax.com:1337 -U "country-us:pk_abc123" https://checker.soax.com/api/ipinfo
```
The username (`country-us` above) carries your rules — where to exit, whether to hold a session, how to handle errors. The password is your package key. They're joined by a colon and sent on every request. See [Authentication](/getting-started/authentication) for the full format.
You should get a JSON response like this:
```json theme={null}
{
"status": true,
"data": {
"ip": "185.123.45.67",
"country_code": "US",
"country_name": "United States",
"region": "California",
"city": "Los Angeles",
"isp": "Spectrum",
"carrier": ""
}
}
```
The `ip` field should be different from your real IP, and the `country_code` should match what you targeted. That means your request was routed through a SOAX proxy node.
You can find your proxy credentials in the dashboard under **Quick Connect**. The username and password are the same whether you connect through the dashboard or the terminal.
## 4. Verify it's working
A successful proxy request means:
* You get a `200` response with `"status": true` (not a timeout or authentication error).
* The `ip` in the response is different from your own.
* If you set a country target, the `country_code` matches what you requested.
If something went wrong, check the [Connection debugging](/troubleshooting/connection-debugging) page for common issues, or review [Error codes](/troubleshooting/error-codes) if you received a specific error.
## Next steps
Understand the difference between residential and mobile proxies.
Learn about username/password auth and IP Auth.
Working Python code for residential and mobile proxies.
Working Node.js code for residential and mobile proxies.
# What is SOAX
Source: https://developers.soax.com/getting-started/what-is-soax
SOAX is web access infrastructure for products that depend on fresh public-web data. Start here for the category, the platform, and where to go next.
**Documentation index:** Fetch the complete index at [developers.soax.com/llms.txt](https://developers.soax.com/llms.txt) to discover all available pages.
SOAX is **web access infrastructure**: one platform that helps products access the public web reliably, at scale, in a world where the rules, defenses, and supply keep changing.
In practice, that means you send your request through SOAX and it routes through one of our nodes instead of your own IP. The target website sees the node's address, not yours. This lets you collect data at scale, access geo-restricted content, and avoid IP bans that would otherwise stop your requests.
## What we offer
SOAX provides two proxy types today, plus two additional product surfaces in **closed alpha**.
### Proxies
**Residential proxies** route your traffic through real home internet connections. We maintain a pool of over 155 million residential IPs across 195+ countries. These IPs belong to genuine ISP subscribers, which makes them very difficult for websites to detect and block. Residential proxies are the best choice for most web scraping, ad verification, and price monitoring tasks.
**Mobile proxies** route traffic through real mobile carrier connections on 3G, 4G, and 5G networks. The mobile pool includes over 30 million IPs from carriers worldwide. They're useful when you need to access mobile-specific content or when your target site treats mobile traffic differently from desktop.
Not sure which one fits your use case? See [Choosing the right proxy type](/getting-started/choosing-proxy-type).
### In closed alpha
**Headful Browser** gives you stateful browser runtimes co-located with our exit nodes. It's shaped for:
* Long-horizon agent execution where each step waits on the previous response and latency compounds.
* JavaScript-heavy or DOM-stateful targets that need a real browser, not a transport-level request.
* Workflows that need cookies, scroll position, modal stacks, or conversation context preserved across multiple actions.
**Web Data API** provides structured retrieval for common public-web targets, so you can request data directly without writing or maintaining parsers. It's shaped for:
* Web-data API products reselling structured retrieval to their own developers.
* Pipelines where parsing, deduplication, and schema management are non-trivial engineering work you'd rather not own.
* AI products grounding inference on a small number of high-cardinality target shapes.
To trial either surface, get in touch via your existing account contact or email `partnerships@soax.com`.
**What "closed alpha" means here:**
* **Not a feature flag on your existing plan.** You can't toggle Headful Browser or Web Data API from the dashboard the way you toggle mobile network on a package. Both run on separate provisioning today.
* **Direct contact with engineering, not just Sales.** Closed-alpha customers work with the product team during integration, share workload telemetry that informs the GA scope, and get visibility into the roadmap. The trade-off is that the contract surface is still evolving: endpoint signatures, schema shapes, and rate-limit behavior may change before GA.
## How it works
You connect to SOAX using standard proxy protocols: HTTP, HTTPS, and SOCKS5. There's nothing proprietary to install. If your tool or code supports proxy connections, it works with SOAX.
Every connection points at the same gateway:
```
proxy.soax.com:1337
```
A connection string has three parts: rules, a package key, and that gateway.
```
country-us-session-job42:pk_abc123@proxy.soax.com:1337
```
* The **rules** (`country-us-session-job42`) describe what you want: where to exit, whether to hold a session, how to handle errors. They're sent as the proxy username.
* The **package key** (`pk_abc123`) authenticates you. It's sent as the proxy password.
* The **gateway** is what you connect to.
You put the rules straight in the connection string — there's nothing to configure in the dashboard first. The dashboard's [Quick Connect](/dashboard/quick-connect) builder can generate the string for you, or you can write it by hand.
You can manage everything from the [dashboard](/dashboard/quick-connect) or integrate directly using your preferred language. We have working examples for [Python](/examples/python), [Node.js](/examples/nodejs), [Go](/examples/go), and [curl](/examples/curl).
## Who SOAX is for
SOAX is built for **products with public-web workloads**: companies whose product depends on fresh public-web data as a runtime input. Their customers feel it when data is stale or fails. Typical workloads include:
* RAG-grounded LLM products, AI search, and agents that operate on the public web.
* Web-data APIs and infrastructure sold to other developers and products.
* Marketing, SEO, and competitive-intelligence products where continuous crawl is the product surface.
* Pricing intelligence, ad verification, threat intelligence, and brand protection embedded inside an AI or SaaS product.
* High-scale e-commerce monitoring and regulatory monitoring.
You'll get the most out of SOAX if you need:
* **High success rates on difficult targets.** Residential and mobile IPs come from real consumer connections, which makes them much harder for anti-bot systems to flag.
* **Granular session control.** Rotate on every request, hold a session for a multi-step task, rotate on a timer or request count, or lock to a single node, all configured in the connection string.
* **Scale without infrastructure overhead.** You don't need to build or maintain your own proxy infrastructure. We handle the pool, rotation, and node health.
## Next steps
Make your first proxy request in under 5 minutes.
Compare proxy types and pick the right one for your use case.
# Mobile Proxies
Source: https://developers.soax.com/proxies/mobile
Full reference for SOAX mobile proxies. Covers how mobile routing works, session types, geo-targeting, connection parameters, and limits.
Mobile proxies route your traffic through real mobile carrier connections on 3G, 4G, and 5G networks. The IPs come from carriers like Verizon, AT\&T, Deutsche Telekom, and others worldwide.
SOAX's mobile pool includes over 30 million IPs from carriers across 195+ countries.
## How mobile proxies are different
Mobile proxies use the same parameter system, session model, and connection format as [residential proxies](/proxies/residential). The difference is where the traffic exits.
With residential proxies, your requests go through home WiFi connections. With mobile proxies, they go through cellular carrier connections. This matters because:
**Mobile IPs are naturally shared.** Carriers use NAT (network address translation) to share IP addresses across many subscribers. This means websites can't easily block a mobile IP without risking blocking thousands of real users. As a result, mobile IPs tend to have lower detection and block rates than residential IPs on heavily protected targets.
**Some content is mobile-specific.** Certain websites and apps serve different content, pricing, or ads to mobile users. Mobile proxies let you see exactly what a mobile visitor would see.
**Carrier metadata is different.** Mobile IPs carry carrier and network type information (3G/4G/5G) rather than home ISP information. This can be relevant for ad verification, app testing, or any workflow where the connection type matters to the target.
## When to use mobile proxies
Mobile proxies are the right choice when:
* Your target site aggressively blocks residential IPs but is more lenient with mobile traffic.
* You need to verify mobile ads, app content, or carrier-specific behavior.
* You're working with social media platforms that scrutinize connection types.
* You need the lowest possible detection rate and are willing to work with a smaller pool.
For general-purpose scraping, residential proxies are a good starting point. If you're hitting high block rates on specific targets, switching to mobile is worth testing.
## Session types
Session behavior is identical to residential proxies. You can use **rotating** (new IP every request) or **ephemeral sessions** (same IP across multiple requests).
### Rotating (no session)
Every request gets a new mobile node. No state between requests.
```bash theme={null}
curl -x proxy.soax.com:1337 -U "network-mob-country-us:YOUR_PACKAGE_KEY" https://checker.soax.com/api/ipinfo
```
### Ephemeral sessions (held IP)
Include a `session` parameter to bind to a node and reuse the same IP across requests.
```bash theme={null}
curl -x proxy.soax.com:1337 -U "network-mob-country-us-session-mobile1:YOUR_PACKAGE_KEY" https://checker.soax.com/api/ipinfo
```
**Session lifetime:**
* Sessions expire after **60 seconds of inactivity** (no requests sent).
* If the bound node becomes unavailable, the system replaces it based on your error handling rules.
**Session ID rules:**
* You choose the session ID (any string you want).
* Allowed characters: letters, digits, and underscores.
* Maximum length: 32 characters.
* Rules are locked on the first request for a given session ID. If you send a second request with the same session ID but different rules, the request is rejected with `409 SESSION_PARAMS_MISMATCH`. To change rules, use a new session ID.
## Geo-targeting
Geo-targeting works the same way as residential. You can filter by country, region, city, carrier (using the `isp` parameter), ASN, or zip code.
| Parameter | Description | Format | Example |
| --------- | ------------------------ | -------------------------------- | -------------------------------------- |
| `country` | Country filter | ISO 3166-1 alpha-2, lowercase | `country-us`, `country-de` |
| `region` | Region or state | Name with underscores for spaces | `region-california`, `region-bavaria` |
| `city` | City | Name with underscores for spaces | `city-new_york`, `city-berlin` |
| `isp` | Mobile carrier | Name with underscores for spaces | `isp-verizon_wireless`, `isp-t_mobile` |
| `asn` | Autonomous system number | ASN number | `asn-6167` |
| `zip` | Postal / zip code | Zip code | `zip-10001` |
Use `country-any` if you want the full global pool without geographic filtering.
**Example: target T-Mobile subscribers in New York:**
```bash theme={null}
curl -x proxy.soax.com:1337 -U "network-mob-country-us-city-new_york-isp-t_mobile:YOUR_PACKAGE_KEY" https://checker.soax.com/api/ipinfo
```
The mobile pool is smaller than the residential pool (30M vs 155M). Very specific targeting (small city + specific carrier) may result in fewer available nodes. If you get `502 NODE_NOT_FOUND` errors, try broadening your filters.
## Rotation rules
Rotation rules control when the system replaces the current node during a session. These work identically to residential.
| Parameter | Description | Example | Max |
| ------------------- | ----------------------------------------------------------------- | -------------------------------------------------------- | --------- |
| `rotate-timed_N` | Replace the node after N seconds (accepts `s`, `m`, `h` suffixes) | `rotate-timed_300` (every 5 minutes), `rotate-timed_10m` | 1 week |
| `rotate-requests_N` | Replace the node after N requests | `rotate-requests_50` (every 50 requests) | 1,000,000 |
Rotation rules require a session. Without a `session` parameter, every request already gets a new node.
**Example: rotate mobile IP every 10 minutes:**
```bash theme={null}
curl -x proxy.soax.com:1337 -U "network-mob-country-us-session-m1-rotate-timed_600:YOUR_PACKAGE_KEY" https://checker.soax.com/api/ipinfo
```
Timed rotation doesn't interrupt requests that are already in progress. The current request finishes on the current node. The next request gets a new node.
## Error handling
Error handling rules define what happens when a node fails during a session. These apply to infrastructure-level failures only (node offline, connection refused, timeout). Target website responses (HTTP status codes) are not visible to the proxy for HTTPS traffic.
| Parameter | Description |
| ----------------- | -------------------------------------------------------------------------------- |
| `onerror-replace` | Get a new eligible node. This is the default. |
| `onerror-retry_N` | Retry the request up to N times on the same node, then replace. Maximum N is 10. |
| `onerror-fail` | Return the error to your client. No retry, no replacement. |
**Example: retry twice, then replace:**
```bash theme={null}
curl -x proxy.soax.com:1337 -U "network-mob-country-us-session-m1-onerror-retry_2:YOUR_PACKAGE_KEY" https://checker.soax.com/api/ipinfo
```
## Routing preferences
Routing preferences affect which node the system picks when it needs to select or replace a node.
| Parameter | Description |
| ------------------ | -------------------------------------------------------------------------------------------- |
| `prefer-lookalike` | Prefer a node with similar characteristics to the previous one (same carrier, region, etc.). |
This is useful when you want IP changes to feel gradual. If your session was using a Verizon IP in Texas and the node goes offline, `prefer-lookalike` tells the system to look for another Verizon IP in Texas before falling back to any eligible mobile node.
In the dashboard, this is the **Prefer lookalike** toggle in the Quick Connect (proxy generator) panel.
**Example:**
```bash theme={null}
curl -x proxy.soax.com:1337 -U "network-mob-country-us-session-m1-onerror-replace-prefer-lookalike:YOUR_PACKAGE_KEY" https://checker.soax.com/api/ipinfo
```
## Node binding
If you need strict binding to a single node with no automatic replacement, use `bind-node`.
| Parameter | Description |
| ----------- | ----------------------------------------------------------------------------------------------------- |
| `bind-node` | Lock the session to a single node. If the node fails, requests fail immediately instead of replacing. |
When `bind-node` is active:
* The session is locked to the node assigned on the first request.
* Rotation rules are ignored.
* If the node fails, you get an error (`503 BOUND_NODE_FAILED`) instead of a replacement.
* You can combine it with `onerror-retry_N` to retry on the same node before failing.
* Requires a `session` parameter.
**Example:**
```bash theme={null}
curl -x proxy.soax.com:1337 -U "network-mob-country-us-session-locked1-bind-node:YOUR_PACKAGE_KEY" https://checker.soax.com/api/ipinfo
```
## Network type parameter
To request mobile nodes, include `network-mob` in your connection string. If your proxy package has both residential and mobile enabled and you don't specify a network type, the default is `res` (residential). To use mobile, you need to set it explicitly.
| Parameter | Description | Values |
| --------- | ------------------- | ----------------------------------------------------------------------- |
| `network` | Network type filter | `res` (residential), `mob` (mobile), `res_mob` (both), `any` (weighted) |
## Full parameter reference
Every parameter available for mobile proxies via Quick Connect:
| Parameter | Description | Values | Default | Requires session |
| ------------------- | ----------------------------------- | ------------------------------------------------- | ---------------- | ---------------- |
| `country` | Country filter | ISO 3166-1 alpha-2 (lowercase), `any` | `any` | No |
| `region` | Region/state filter | Name (underscores for spaces) | any | No |
| `city` | City filter | Name (underscores for spaces) | any | No |
| `isp` | Carrier filter | Name (underscores for spaces) | any | No |
| `asn` | ASN filter | ASN number | any | No |
| `zip` | Postal / zip filter | Zip code | any | No |
| `network` | Network type | `res`, `mob`, `any`, or combined (e.g. `res_mob`) | `res` | No |
| `session` | Session identifier | Letters, digits, underscores. Max 32 chars. | (none, rotating) | No |
| `rotate-timed_N` | Rotate after N (`s`/`m`/`h` suffix) | Up to 1 week | (keep node) | Yes |
| `rotate-requests_N` | Rotate after N requests | Up to 1,000,000 | (keep node) | Yes |
| `onerror` | Error handling | `replace`, `retry_N` (max 10), `fail` | `replace` | Yes |
| `prefer` | Routing preference | `lookalike` | (none) | Yes |
| `bind-node` | Lock to single node | Flag (no value) | off | Yes |
**Connection string format:**
```
{rules}:{package_key}@proxy.soax.com:1337
```
Rules are separated by hyphens. Multi-word values use underscores. Rules can appear in any order.
## Example configurations
**Basic rotating proxy, US mobile:**
```
network-mob-country-us:YOUR_PACKAGE_KEY@proxy.soax.com:1337
```
**Session, German mobile, 10-minute rotation:**
```
network-mob-country-de-session-de1-rotate-timed_600:YOUR_PACKAGE_KEY@proxy.soax.com:1337
```
**Session with retry on error, then replace with lookalike:**
```
network-mob-country-us-session-m1-onerror-retry_3-prefer-lookalike:YOUR_PACKAGE_KEY@proxy.soax.com:1337
```
**Specific carrier and city:**
```
network-mob-country-us-city-los_angeles-isp-verizon_wireless-session-v1:YOUR_PACKAGE_KEY@proxy.soax.com:1337
```
**Locked to a single mobile node:**
```
network-mob-country-us-session-locked1-bind-node:YOUR_PACKAGE_KEY@proxy.soax.com:1337
```
## Limits
| Constraint | Value |
| ------------------------------------ | -------------------------------------------------------------------------------------- |
| Session ID max length | 32 characters |
| Session ID characters | Letters, digits, underscores |
| Ephemeral session inactivity timeout | 60 seconds |
| Concurrent sessions | Package-dependent |
| RPS limit | Package-dependent |
| Concurrent connections | 4,000 per proxy gateway on the Sandbox plan; higher limits can be evaluated on request |
| `rotate-timed_N` max | 1 week |
| `rotate-requests_N` max | 1,000,000 |
| `onerror-retry_N` max | 10 |
| DNS label limit (IP Auth over HTTPS) | 63 characters |
## Residential vs mobile: quick comparison
| | Residential | Mobile |
| -------------------- | --------------------------------------- | ---------------------------------------------------------- |
| IP source | Home ISP connections | Mobile carrier connections (3G/4G/5G) |
| Pool size | 155M+ | 30M+ |
| Detection risk | Low | Very low |
| Best for | General scraping, price monitoring, SEO | Mobile content, ad verification, heavily protected targets |
| Default network type | Yes (`res`) | No (must specify `network-mob`) |
| Credit cost per tier | Same | Same |
| Parameter system | Identical | Identical |
## Next steps
Full reference for residential proxy configuration.
How to authenticate with username/password or IP Auth.
Full list of SOAX error codes with causes and fixes.
Working Python code for mobile proxies.
# Residential Proxies
Source: https://developers.soax.com/proxies/residential
Full reference for SOAX residential proxies. Covers how routing works, session types, geo-targeting, connection parameters, and limits.
Residential proxies route your traffic through real home internet connections. The IPs belong to genuine ISP subscribers, which makes them difficult for websites to detect and block.
SOAX's residential pool includes over 155 million IPs across 195+ countries, with targeting available down to city level.
## How residential routing works
When you send a request through SOAX, the system selects a node from the residential pool that matches your targeting parameters (country, city, ISP, etc.). Your request is sent through that node to the target website, and the response comes back to you.
The target website sees the node's residential IP address, not yours. Because these IPs are assigned by real ISPs to real households, most anti-bot systems treat them as legitimate traffic.
You control which nodes are eligible for your requests using filtering parameters, and you control how long you stay on the same node using session and rotation parameters.
## Session types
There are two ways to use residential proxies: **rotating** (new IP every request) and **sessions** (keep the same IP across multiple requests).
### Rotating (no session)
When you don't include a `session` parameter, every request gets a new node. No state is maintained between requests. This is the simplest mode and works well for high-volume scraping where you don't need IP continuity.
```bash theme={null}
curl -x proxy.soax.com:1337 -U "country-us:YOUR_PACKAGE_KEY" https://checker.soax.com/api/ipinfo
```
Each time you run this, you'll get a different IP.
### Ephemeral sessions (held IP)
When you include a `session` parameter, the system binds your session to a node. All subsequent requests with the same session ID reuse that node, giving you the same IP.
```bash theme={null}
curl -x proxy.soax.com:1337 -U "country-us-session-job1:YOUR_PACKAGE_KEY" https://checker.soax.com/api/ipinfo
```
Run this multiple times and you'll see the same IP, as long as the session is active and the node is available.
**Session lifetime:**
* Sessions expire after **60 seconds of inactivity** (no requests sent).
* If the bound node becomes unavailable, the system replaces it based on your error handling rules.
**Session ID rules:**
* You choose the session ID (any string you want).
* Allowed characters: letters, digits, and underscores.
* Maximum length: 32 characters.
* Rules are locked on the first request for a given session ID. If you send a second request with the same session ID but different rules, the request is rejected with `409 SESSION_PARAMS_MISMATCH`. To change rules, use a new session ID.
## Geo-targeting
You can target residential nodes by country, region, city, ISP, or ASN. These parameters filter which nodes are eligible for your requests.
| Parameter | Description | Format | Example |
| --------- | ------------------------- | -------------------------------- | -------------------------------------- |
| `country` | Country filter | ISO 3166-1 alpha-2, lowercase | `country-us`, `country-gb` |
| `region` | Region or state | Name with underscores for spaces | `region-california`, `region-new_york` |
| `city` | City | Name with underscores for spaces | `city-los_angeles`, `city-austin` |
| `isp` | Internet service provider | Name with underscores for spaces | `isp-comcast`, `isp-verizon_wireless` |
| `asn` | Autonomous system number | ASN number | `asn-7922` |
| `zip` | Postal / zip code | Zip code | `zip-90210` |
Use `country-any` if you want the full global pool without geographic filtering. You need at least one targeting parameter in every request.
You can combine multiple filters. For example, to target Comcast subscribers in Los Angeles:
```bash theme={null}
curl -x proxy.soax.com:1337 -U "country-us-city-los_angeles-isp-comcast:YOUR_PACKAGE_KEY" https://checker.soax.com/api/ipinfo
```
The more specific your targeting, the smaller the eligible pool. If you target a small city with a specific ISP, there may be fewer available nodes, which can affect speed and availability. Start broad and narrow down as needed.
## Rotation rules
Rotation rules control when the system replaces the current node during a session. Without a rotation rule, the session keeps the same node until it expires or encounters an error.
| Parameter | Description | Example | Max |
| ------------------- | ----------------------------------------------------------------- | ------------------------------------------------------- | --------- |
| `rotate-timed_N` | Replace the node after N seconds (accepts `s`, `m`, `h` suffixes) | `rotate-timed_300` (every 5 minutes), `rotate-timed_5m` | 1 week |
| `rotate-requests_N` | Replace the node after N requests | `rotate-requests_50` (every 50 requests) | 1,000,000 |
Rotation rules require a session. Without a `session` parameter, every request already gets a new node, so rotation doesn't apply.
**Example: rotate IP every 5 minutes within a session:**
```bash theme={null}
curl -x proxy.soax.com:1337 -U "country-us-session-scrape1-rotate-timed_300:YOUR_PACKAGE_KEY" https://checker.soax.com/api/ipinfo
```
Timed rotation doesn't interrupt requests that are already in progress. If a request is running when the timer expires, it finishes on the current node. The next request gets a new node.
## Error handling
Error handling rules define what happens when a node fails during a session (goes offline, connection refused, timeout). These apply to infrastructure-level failures only. Target website responses (HTTP status codes like 403 or 429) are not visible to the proxy for HTTPS traffic and aren't handled by these rules.
| Parameter | Description |
| ----------------- | -------------------------------------------------------------------------------- |
| `onerror-replace` | Get a new eligible node. This is the default. |
| `onerror-retry_N` | Retry the request up to N times on the same node, then replace. Maximum N is 10. |
| `onerror-fail` | Return the error to your client. No retry, no replacement. |
**Example: retry 3 times on the same node before replacing:**
```bash theme={null}
curl -x proxy.soax.com:1337 -U "country-us-session-job1-onerror-retry_3:YOUR_PACKAGE_KEY" https://checker.soax.com/api/ipinfo
```
## Routing preferences
Routing preferences affect which node the system picks when selecting or replacing a node. They influence preference, not eligibility. Filtering parameters decide which nodes are eligible; routing preferences decide which eligible node is chosen.
| Parameter | Description |
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `prefer-lookalike` | When the system needs to select a new node (on rotation, error replacement, or initial allocation), prefer a node with similar characteristics to the previous one (same region, ISP, etc.). |
This is useful when you want IP changes to be gradual rather than random. For example, if your session was using a Comcast IP in California and the node goes offline, `prefer-lookalike` tells the system to look for another Comcast IP in California before falling back to any eligible node.
In the dashboard, this is the **Prefer lookalike** toggle in the Quick Connect (proxy generator) panel.
**Example:**
```bash theme={null}
curl -x proxy.soax.com:1337 -U "country-us-session-job1-onerror-replace-prefer-lookalike:YOUR_PACKAGE_KEY" https://checker.soax.com/api/ipinfo
```
## Node binding
By default, sessions can replace their node when rotation or error handling rules allow it. If you need strict binding to a single node with no automatic replacement, use `bind-node`.
| Parameter | Description |
| ----------- | ----------------------------------------------------------------------------------------------------- |
| `bind-node` | Lock the session to a single node. If the node fails, requests fail immediately instead of replacing. |
When `bind-node` is active:
* The session is locked to the node assigned on the first request.
* Rotation rules are ignored.
* If the node fails, you get an error (`503 BOUND_NODE_FAILED`) instead of a replacement.
* You can combine it with `onerror-retry_N` to retry on the same node before failing.
* Requires a `session` parameter.
**Example:**
```bash theme={null}
curl -x proxy.soax.com:1337 -U "country-us-session-locked1-bind-node:YOUR_PACKAGE_KEY" https://checker.soax.com/api/ipinfo
```
## Network type parameter
To explicitly request residential nodes, use `network-res`. This is the default when no network parameter is provided, so you don't need to include it unless your proxy package has both residential and mobile enabled and you want to restrict to residential only.
| Parameter | Description | Values |
| --------- | ------------------- | ----------------------------------------------------------------------- |
| `network` | Network type filter | `res` (residential), `mob` (mobile), `res_mob` (both), `any` (weighted) |
## Full parameter reference
Here's every parameter available for residential proxies via Quick Connect:
| Parameter | Description | Values | Default | Requires session |
| ------------------- | ----------------------------------- | ------------------------------------------------- | ---------------- | ---------------- |
| `country` | Country filter | ISO 3166-1 alpha-2 (lowercase), `any` | `any` | No |
| `region` | Region/state filter | Name (underscores for spaces) | any | No |
| `city` | City filter | Name (underscores for spaces) | any | No |
| `isp` | ISP filter | Name (underscores for spaces) | any | No |
| `asn` | ASN filter | ASN number | any | No |
| `zip` | Postal / zip filter | Zip code | any | No |
| `network` | Network type | `res`, `mob`, `any`, or combined (e.g. `res_mob`) | `res` | No |
| `session` | Session identifier | Letters, digits, underscores. Max 32 chars. | (none, rotating) | No |
| `rotate-timed_N` | Rotate after N (`s`/`m`/`h` suffix) | Up to 1 week | (keep node) | Yes |
| `rotate-requests_N` | Rotate after N requests | Up to 1,000,000 | (keep node) | Yes |
| `onerror` | Error handling | `replace`, `retry_N` (max 10), `fail` | `replace` | Yes |
| `prefer` | Routing preference | `lookalike` | (none) | Yes |
| `bind-node` | Lock to single node | Flag (no value) | off | Yes |
**Connection string format:**
```
{rules}:{package_key}@proxy.soax.com:1337
```
Rules are separated by hyphens. Multi-word values use underscores. Rules can appear in any order.
If you're passing rules in the subdomain for IP Auth over HTTPS, watch the 63-character DNS label limit. Use the shortcut forms in the table below (for example, `c-us-ci-new_york-s-job1` instead of `country-us-city-new_york-session-job1`) if your config is too long.
### Parameter shortcuts
To keep a connection string short — most often to stay under the 63-character DNS label limit with IP Auth over HTTPS — you can use the short form of each parameter name. The long and short forms are interchangeable and can be mixed in the same string.
| Parameter | Shortcut |
| ------------ | -------- |
| `country` | `c` |
| `region` | `r` |
| `city` | `ci` |
| `isp` | `i` |
| `asn` | `a` |
| `zip` | `z` |
| `network` | `n` |
| `session` | `s` |
| `subaccount` | `sa` |
| `onerror` | `oe` |
For example, `c-us-ci-new_york-s-job1` is equivalent to `country-us-city-new_york-session-job1`.
## Example configurations
**Basic rotating proxy, US:**
```
country-us:YOUR_PACKAGE_KEY@proxy.soax.com:1337
```
**Session, UK, 5-minute rotation:**
```
country-gb-session-uk1-rotate-timed_300:YOUR_PACKAGE_KEY@proxy.soax.com:1337
```
**Session with retry on error, then replace with lookalike:**
```
country-us-session-scrape1-onerror-retry_3-prefer-lookalike:YOUR_PACKAGE_KEY@proxy.soax.com:1337
```
**Specific city and ISP, mobile network:**
```
network-mob-country-us-city-new_york-session-m1:YOUR_PACKAGE_KEY@proxy.soax.com:1337
```
**Locked to a single node (no replacement on failure):**
```
country-us-session-locked1-bind-node:YOUR_PACKAGE_KEY@proxy.soax.com:1337
```
## Limits
| Constraint | Value |
| ------------------------------------ | -------------------------------------------------------------------------------------- |
| Session ID max length | 32 characters |
| Session ID characters | Letters, digits, underscores |
| Ephemeral session inactivity timeout | 60 seconds |
| Concurrent sessions | Package-dependent |
| RPS limit | Package-dependent |
| Concurrent connections | 4,000 per proxy gateway on the Sandbox plan; higher limits can be evaluated on request |
| `rotate-timed_N` max | 1 week |
| `rotate-requests_N` max | 1,000,000 |
| `onerror-retry_N` max | 10 |
| DNS label limit (IP Auth over HTTPS) | 63 characters |
## Next steps
Full reference for mobile proxy configuration.
How to authenticate with username/password or IP Auth.
Full list of SOAX error codes with causes and fixes.
Working Python code for residential proxies.
# Account Bans & Protection
Source: https://developers.soax.com/troubleshooting/account-bans-protection
How account bans differ from IP blocks, what triggers them, and how to structure your proxy usage to reduce that risk.
Account bans are different from IP blocks, and they're harder to recover from. This page explains the difference, what causes account bans specifically, and how to set up your proxy usage to minimize the risk.
## Account bans vs IP blocks
It's worth being clear about what we're talking about, because these two things get conflated.
**IP block** — the target website refuses to serve content to a specific IP address or range. Your account isn't affected. Switch to a different IP and your requests go through again.
**Account ban** — the platform bans your account. The IP is irrelevant at that point. You can switch to a clean residential IP and the account still doesn't work, because the ban is tied to the account identity, not the network origin.
Proxies directly solve IP blocks. They only help with account bans indirectly — by making your account's activity look more like a real user and less like an automated system.
## What triggers account bans
Platforms that manage accounts — social media, e-commerce, ad platforms, marketplaces — use a combination of signals to identify and ban suspicious accounts. IP is one signal, but it's rarely the only one.
**IP reputation.** Logging in from an IP with a high fraud score, a known datacenter ASN, or a history of abuse is a strong signal. Residential and mobile IPs score much better here. See [IP quality & fraud score](/troubleshooting/ip-quality-fraud-score).
**IP consistency.** Real users don't log into the same account from five different countries in an hour. If your account switches IPs frequently — especially across different geos — that pattern gets flagged. This is the most common mistake people make when using rotating proxies for account-based work.
**Device and browser fingerprint.** Platforms fingerprint more than just the IP. TLS fingerprint, browser headers, canvas and WebGL rendering, screen resolution, timezone offset, installed fonts — all of these contribute to an identity signal. If the IP says "home user in California" but the fingerprint says "headless Chromium with no plugins", the IP doesn't help.
**Behavioral patterns.** Real users have irregular patterns. They pause, misclick, scroll at variable speeds, and navigate inconsistently. Automated behavior — perfectly timed actions, no mouse movement, navigating directly to specific endpoints — is detectable regardless of IP quality.
**Account age and history.** New accounts with no history that immediately start performing high-volume actions are flagged faster than established accounts. Warming up an account (gradual ramp-up of activity over days or weeks) significantly reduces ban risk.
## How to structure your proxy usage for account work
**Use sessions, not rotating IPs.** This is the most important thing. Each account should have one session with a consistent IP. Rotating IPs is the right strategy for anonymous scraping — it's the wrong strategy for account-based work.
**Match the GEO to the account's registered location.** If the account was created in New York, use a US IP. Ideally, use a New York IP. Logins from unexpected locations are a standard fraud signal.
**Use mobile proxies for platforms that scrutinize connection type.** Social media platforms in particular treat mobile carrier IPs differently from residential WiFi IPs — both score well, but mobile IPs are shared across large numbers of real users by design, which makes them harder to flag individually.
**Don't share sessions across accounts.** Each account should have its own session ID. If you're running multiple accounts through the same IP, platforms that track IP-to-account ratios will flag that pattern.
```python theme={null}
# Separate session per account
accounts = ["account_1", "account_2", "account_3"]
for account in accounts:
proxy_url = f"http://country-us-session-{account}:YOUR_PACKAGE_KEY@proxy.soax.com:1337"
# Use this proxy for all requests associated with this account
```
**Keep sessions alive.** Sessions expire after 60 seconds of inactivity. If a session expires and a new node is allocated for the same account, you're effectively logging in from a different IP. Build in keepalive requests if your workflow has gaps longer than 60 seconds.
## What proxies can't fix
Proxies solve the IP dimension of account ban detection. They don't help with:
* **Browser fingerprint.** If you're using browser automation, use a tool that generates realistic fingerprints. Headless Chromium with default settings is trivially detectable on most platforms.
* **Behavioral patterns.** Automated scripts that navigate too fast, too consistently, or without realistic mouse/scroll patterns will get flagged even with clean IPs.
* **Account history.** A newly created account that immediately starts bulk activity is suspicious regardless of what IP it uses.
Proxies are one layer of a detection-avoidance strategy, not the whole strategy.
## When an account gets banned
If an account gets banned, changing the IP won't unban it. Your options are:
* Appeal through the platform's official process (effective for false positives on legitimate accounts).
* Accept the ban and start fresh with a new account — this time with better practices from the start.
Don't reuse the same session or IP for a new account immediately after a ban. Some platforms track IP history and will fast-track bans on accounts that appear from the same IP as a recently banned one.
## Next steps
Understand what makes an IP trustworthy and how SOAX maintains pool quality.
What to do when the target site is blocking your requests.
Session, rotation, and error handling parameter reference.
Lower detection rates for platforms that scrutinize connection type.
# CAPTCHA & Ban Rates
Source: https://developers.soax.com/troubleshooting/captcha-ban-rates
What to expect from CAPTCHA and ban rates when using SOAX proxies. Learn the difference between proxy issues and target-site detection, and how to improve success rates.
If you're collecting data from the web at scale, you'll encounter CAPTCHAs and blocks. This is normal. It's not a sign that your proxies are broken. It's a sign that the target website is doing its job.
This page explains what causes detection, what SOAX can and can't control, and what you can do to improve your success rates.
## How detection works
Websites use anti-bot systems to identify and block automated traffic. These systems look at signals like request frequency, browser fingerprints, header patterns, behavioral cues, and IP reputation.
When a website detects something suspicious, it typically responds in one of these ways:
* Serving a CAPTCHA challenge instead of the page content
* Returning an HTTP 403 (Forbidden) or 429 (Too Many Requests) response
* Returning a fake or empty page that looks normal but contains no real data
* Silently rate-limiting your requests so they slow down or time out
* Blocking the IP address entirely so future requests fail
The important thing to understand is that these decisions are made by the target website, not by SOAX. The proxy's job is to route your request through a clean IP. What the target does with that request is outside the proxy's control.
## What SOAX can and can't see
SOAX proxies use CONNECT tunnels for HTTPS traffic. This means the proxy establishes a connection to the target on your behalf, but it can't read or inspect the encrypted traffic that flows through it.
In practice, this means:
* SOAX **can** detect infrastructure failures: the node is offline, the connection was refused, the request timed out before the tunnel was established.
* SOAX **can't** see HTTP status codes from the target (403, 429, 503, etc.) because they're inside the encrypted tunnel.
* SOAX **can't** detect CAPTCHAs, soft blocks, or fake pages because those are part of the HTTP response body, which is encrypted.
This is why the [error handling rules](/proxies/residential#error-handling) (`onerror-replace`, `onerror-retry_N`, `onerror-fail`) only respond to infrastructure-level failures, not target-level blocks. SOAX works at the transport layer; if a target returns a CAPTCHA page with an HTTP 200 status, the proxy sees a successful connection and passes it through unchanged.
Detecting and handling target-side blocks is your application's responsibility.
## What's a normal CAPTCHA / block rate?
There's no universal answer. It depends entirely on the target website, the volume of your requests, and how your scraper behaves. Here are some general ranges:
**Low-protection targets** (most news sites, public directories, open APIs): Block rates under 1% are typical. Residential IPs rarely get challenged on these sites.
**Medium-protection targets** (ecommerce product pages, review sites, job boards): Block rates between 2% and 10% are common, depending on your request volume and patterns.
**High-protection targets** (Google, Amazon, social media platforms, sneaker sites): Block rates of 10% to 30% or higher are expected. These sites invest heavily in anti-bot systems and actively fingerprint incoming traffic. Even real users occasionally see CAPTCHAs on these sites.
If your block rate on a specific target is significantly higher than these ranges, it's usually a sign that something in your request pattern is triggering detection, not that the proxies are bad.
## How to improve success rates
### Use the right network type
Mobile proxies generally have lower block rates than residential on heavily protected targets. This is because mobile carrier IPs are shared across many real users via NAT, making them harder to block without affecting legitimate traffic. If residential IPs are getting blocked on your target, try `network-mob`.
### Rotate IPs appropriately
Sending too many requests from the same IP is the most common trigger for blocks. For high-volume scraping, use rotating mode (no session parameter) so every request gets a fresh IP.
If you need sessions for multi-page workflows (login, navigate, scrape), use `rotate-timed_N` to force a fresh IP periodically. A common pattern is rotating every 3 to 5 minutes.
### Pace your requests
Sending hundreds of requests per second to the same domain is the fastest way to get blocked, regardless of how clean your IPs are. Anti-bot systems track request frequency per IP, per subnet, and per behavioral pattern.
Add reasonable delays between requests. Even 1 to 2 seconds between requests to the same domain significantly reduces detection risk.
### Vary your request patterns
Anti-bot systems look for patterns that real users don't produce:
* Requesting the same page structure repeatedly without variation
* Missing standard browser headers (User-Agent, Accept, Accept-Language, etc.)
* Sending requests in perfectly regular intervals (exactly every N seconds)
* Never loading CSS, images, or JavaScript (for browser-based checks)
Make sure your scraper sends realistic headers and varies its timing slightly between requests.
### Target broader geos
Sending 1,000 requests per minute from IPs in a single city looks suspicious. Spreading your traffic across a wider geographic area (country-level instead of city-level) gives you access to more IPs and makes your traffic pattern look more natural.
### Handle blocks in your application
Since SOAX can't see target-side responses, your application needs to detect and react to blocks:
* Check the HTTP status code of every response. A 403, 429, or unexpected 200 with CAPTCHA content means you've been detected.
* When you detect a block, rotate to a new IP by using a new session ID or switching to rotating mode.
* Track your success rate per target. If it drops below your threshold, slow down, switch network types, or broaden your geo-targeting.
* Consider exponential backoff when you detect rate-limiting (429 responses). Hammering a target that's already blocking you only makes it worse.
## Common misconceptions
**"I'm getting blocked, so the IPs must be dirty."** Not necessarily. Even fresh, never-before-used IPs get blocked if the request pattern triggers detection. Clean IPs with bad request patterns get blocked faster than "used" IPs with good patterns.
**"Residential IPs should never get blocked."** Residential IPs are harder to detect than datacenter IPs, but they're not invisible. Anti-bot systems have evolved beyond simple IP classification. They look at TLS fingerprints, header ordering, mouse movements (for browser-based checks), and many other signals.
**"Higher success rates mean better proxies."** Success rate depends on target difficulty, request patterns, and scraper quality as much as it depends on IP quality. Two customers using the same SOAX plan on the same target can have very different success rates based on how their scrapers behave.
**"More retries will fix the problem."** Retrying the same request through different IPs helps with transient failures, but it doesn't help if the target is detecting something about your request other than the IP (e.g. headers, fingerprint, behavior). Fix the detection signal first, then retry.
## When to contact support
Reach out to support if:
* Your success rate drops significantly and suddenly across multiple targets (not just one). This could indicate a pool issue rather than target-side detection.
* You're getting SOAX-level errors (407, 429, 502, 503) rather than target-side blocks. These are infrastructure issues that the SOAX team can investigate. See [Error Codes](/troubleshooting/error-codes) for the full list.
* You're seeing IP geo-mismatches where the exit IP doesn't match the country you targeted. This is a routing issue on our side.
If your block rate is high on a single target but everything else works fine, it's almost certainly target-side detection. The suggestions above are your best path forward.
## Next steps
Distinguish between SOAX errors and target-side blocks.
Step-by-step checklist for diagnosing connection issues.
Session, rotation, and error handling parameter reference.
Lower detection rates for heavily protected targets.
# Connection Debugging
Source: https://developers.soax.com/troubleshooting/connection-debugging
Step-by-step checklist for diagnosing SOAX proxy connection issues. Covers timeouts, auth failures, geo mismatches, and common mistakes.
If your proxy requests aren't working, work through this checklist before contacting support. Most issues fall into a few common categories, and you can usually fix them yourself in a few minutes.
## Step 1: Check your authentication
The most common reason requests fail is an authentication problem. Run this test command from your terminal:
```bash theme={null}
curl -x proxy.soax.com:1337 -U "country-any:YOUR_PACKAGE_KEY" https://checker.soax.com/api/ipinfo
```
**If you get a `407 AUTH_FAILED` response:**
* Make sure your package key matches what's shown in the dashboard under Quick Connect. Copy it exactly.
* Check that the key is in the password position, after the colon. The format is `{rules}:{package_key}`. A common mistake is putting the key in the wrong field.
* If you recently regenerated your key, the old one stops working immediately. Update it everywhere.
**If you get a `407 IP_NOT_ALLOWED` response:**
* You're using IP Auth, but your current IP isn't on the allowlist.
* Visit [checker.soax.com/api/ipinfo](https://checker.soax.com/api/ipinfo) directly (without a proxy) to see your public IP, then add it to the allowlist in the dashboard.
* If you're behind a VPN, corporate NAT, or cloud instance, your outbound IP may not be what you expect.
**If you get a `407 PACKAGE_SUSPENDED` response:**
* Your account or package has been suspended. Check the dashboard for notifications, or contact support.
If the test command above returns a valid JSON response with an IP, your authentication is working. Move to the next step.
## Step 2: Check your connection string format
SOAX connection strings use hyphens to separate rules and underscores within multi-word values. Getting this wrong is a common source of silent failures or unexpected behavior.
**Correct:**
```
country-us-city-los_angeles-session-job1:YOUR_PACKAGE_KEY@proxy.soax.com:1337
```
**Wrong (hyphen in city name instead of underscore):**
```
country-us-city-los-angeles-session-job1:YOUR_PACKAGE_KEY@proxy.soax.com:1337
```
The second example parses `los` as the city and `angeles` as an unknown parameter. You won't get an error, but the targeting will be wrong.
**Common formatting mistakes:**
* Using hyphens inside values (`los-angeles` instead of `los_angeles`)
* Missing the colon between rules and package key
* Putting the package key in the username field instead of the password field
* Using `=` instead of `-` to set values (`country=us` instead of `country-us`)
* Forgetting `country-any` when you don't want geo-targeting (at least one targeting rule is required)
## Step 3: Check your geo-targeting
If your requests succeed but return IPs from the wrong country or city, the issue is usually in your targeting parameters.
**Run a targeted request and check the response:**
```bash theme={null}
curl -x proxy.soax.com:1337 -U "country-de-city-berlin:YOUR_PACKAGE_KEY" https://checker.soax.com/api/ipinfo
```
Check the `country_code` and `city` fields in the response. If they don't match what you targeted:
* Make sure the country code is lowercase ISO 3166-1 alpha-2 (e.g. `us`, `gb`, `de`). Common pitfalls: uppercase (`US`), using `uk` instead of the correct code `gb`, or three-letter codes like `GER`.
* Check for typos in region and city names. Use underscores for spaces.
* If you're targeting a very specific location (small city + ISP), the pool may be too small and the system can't find a match. The `X-SOAX-Detail` header will tell you if this is the case. Try removing the most specific filter first (city or ISP) and see if the country matches.
**If you get a `502 NODE_NOT_FOUND` response:**
* No nodes match your filter combination. Broaden your targeting.
* Mobile pools are smaller than residential. If you're using `network-mob`, try `network-res` to see if the issue is mobile-specific.
* Some regions have limited coverage at certain times of day. Try again later.
## Step 4: Check for timeouts
If your requests hang and eventually time out, the issue could be on the proxy side, the target side, or your client configuration.
**Test with a known-good target first:**
```bash theme={null}
curl -x proxy.soax.com:1337 -U "country-us:YOUR_PACKAGE_KEY" https://checker.soax.com/api/ipinfo
```
If this works but your actual target times out, the problem is between the proxy node and the target website, not with SOAX itself.
**If the checker also times out:**
* Check that you can reach `proxy.soax.com` on port 1337 from your network. Some corporate firewalls block non-standard ports.
* Try from a different network (e.g. phone hotspot) to rule out local network issues.
* Check the [SOAX status page](https://status.soax.com) for any ongoing incidents.
**If only your target times out:**
* The target website may be slow or rate-limiting the exit IP. Try a different country or session to get a fresh IP.
* If you get `504 GATEWAY_TIMEOUT`, the proxy connection was established but the target didn't respond in time. Increase the timeout on your HTTP client.
* Some targets block traffic at the TCP level without sending a response. This looks like a timeout from your side. Try rotating to a new IP.
## Step 5: Check your session behavior
If your session isn't holding the same IP across requests, or if sessions are breaking unexpectedly:
**Ephemeral sessions expire after 60 seconds of inactivity.** If there's a gap of more than 60 seconds between requests with the same session ID, the session is gone. The next request creates a new session and gets a new node. If your workflow has natural pauses longer than that, build in keepalive requests to hold the session open.
**Conflicting rules on the same session ID return `409 SESSION_PARAMS_MISMATCH`.** If you send `country-us-session-job1` and then `country-gb-session-job1`, the second request fails because the session was created with US targeting and you're trying to change it. To change rules, use a new session ID.
**Rotation rules force IP changes within a session.** If you set `rotate-timed_300` and your IP changes after 300 seconds, that's working as intended. Remove the rotation rule if you want to keep the same IP for the full session lifetime.
## Step 6: Check your package limits
If requests were working and suddenly stopped:
**Credit exhaustion.** If your organization's credits are used up, requests will be blocked. Check your balance in the dashboard under Usage & Analytics.
**Traffic limit on the package.** If the organization owner set a traffic limit on your proxy package, you'll get `429 TRAFFIC_LIMIT_EXCEEDED` when you hit it. Ask the owner to increase or remove the limit.
**RPS or connection limit.** If you're sending too many requests in parallel, you'll get `429 RATE_LIMIT_EXCEEDED`. Reduce your concurrency or upgrade your plan for higher limits.
## Quick diagnostic checklist
Work through this in order. Stop as soon as you find the issue.
1. Can you reach the checker with `country-any`? If no, it's an auth or network problem (steps 1 and 4).
2. Does the checker return the right country? If no, it's a targeting problem (step 3).
3. Does your actual target work? If no, it's a target-side or timeout problem (step 4).
4. Does your session hold the same IP? If no, check session behavior (step 5).
5. Are you getting 429 errors? Check your limits (step 6).
6. Still not working? Contact support through the dashboard with your `X-SOAX-Error` code and the connection string you're using (with the package key redacted).
When contacting support, include the X-SOAX-Error and X-SOAX-Detail header values from the failed response. These give the support team the fastest path to diagnosing your issue.
## Next steps
Full list of every error code with causes and fixes.
Understanding detection and block rates on target websites.
Make sure your credentials and connection format are correct.
Answers to the most common questions.
# Error Codes & Fixes
Source: https://developers.soax.com/troubleshooting/error-codes
Every error code SOAX returns, what causes it, and how to fix it. Diagnose and resolve issues without opening a support ticket.
When something goes wrong with a proxy request, SOAX returns an HTTP status code along with two custom headers that tell you exactly what happened:
| Header | Description |
| --------------- | ------------------------------------------------ |
| `X-SOAX-Error` | Machine-readable error code (e.g. `AUTH_FAILED`) |
| `X-SOAX-Detail` | Human-readable explanation of what went wrong |
These are SOAX-level errors. SOAX works at the transport layer, so the failures it can act on are infrastructure failures — node offline, connection refused, timeout. HTTP status codes from the target website (e.g. 403, 429, soft-block CAPTCHA pages) live inside the HTTPS tunnel and aren't visible to SOAX. See [CAPTCHA & ban rates](/troubleshooting/captcha-ban-rates) for handling those in your application.
## Validation errors (400)
Your connection string was parsed but something in it wasn't valid. The request never reached node selection.
### ERR\_INVALID\_VALUE
```
HTTP/1.1 400 Bad Request
X-SOAX-Error: ERR_INVALID_VALUE
X-SOAX-Detail: Invalid value for parameter
```
**What it means:** A rule has a value that doesn't pass validation — bad country code, unknown ISP, malformed ASN, and so on.
**How to fix it:**
* Check the `X-SOAX-Detail` for which parameter is wrong.
* Country codes are lowercase ISO 3166-1 alpha-2 (`us`, `gb`, `de`). Not uppercase, not three-letter codes.
* ISP and city names use underscores for spaces (`new_york`, `verizon_wireless`). Available names are listed in your package settings.
### ERR\_MISSING\_PARAMS
```
HTTP/1.1 400 Bad Request
X-SOAX-Error: ERR_MISSING_PARAMS
X-SOAX-Detail: At least one targeting parameter is required
```
**What it means:** You didn't include any targeting rule. The auth RFCs require something in the username field.
**How to fix it:** Add a targeting rule. If you don't care which country you exit from, use `country-any`.
### ERR\_UNKNOWN\_PARAM
```
HTTP/1.1 400 Bad Request
X-SOAX-Error: ERR_UNKNOWN_PARAM
X-SOAX-Detail: Unknown parameter name
```
**What it means:** A rule name in your string isn't recognised. Most common cause: a typo, or using a hyphen inside a multi-word value (so the value's tail looks like an unknown parameter name).
**How to fix it:**
* Check spelling against the [parameter reference](/proxies/residential#full-parameter-reference).
* Make sure multi-word values use underscores: `city-los_angeles`, not `city-los-angeles`.
### ERR\_DUPLICATE\_PARAM
```
HTTP/1.1 400 Bad Request
X-SOAX-Error: ERR_DUPLICATE_PARAM
X-SOAX-Detail: Parameter specified more than once
```
**What it means:** The same rule appears twice in the connection string (e.g. `country-us-country-gb`).
**How to fix it:** Remove the duplicate. Each rule appears at most once.
### ERR\_INVALID\_COMBINATION
```
HTTP/1.1 400 Bad Request
X-SOAX-Error: ERR_INVALID_COMBINATION
X-SOAX-Detail: Parameters cannot be combined
```
**What it means:** Two rules were used together that contradict each other. The most common case is `bind-node` with `rotate-timed_N`, `rotate-requests_N`, `onerror-replace`, or `prefer-lookalike` — `bind-node` is incompatible with all of those.
**How to fix it:** Drop one side of the conflict. See the [`bind-node` interaction table](/proxies/residential#node-binding) for the full list.
### ERR\_PARAM\_NOT\_ALLOWED
```
HTTP/1.1 400 Bad Request
X-SOAX-Error: ERR_PARAM_NOT_ALLOWED
X-SOAX-Detail: Parameter not permitted on this package
```
**What it means:** A rule isn't permitted on this package or in this context — for example, a `network` value the package doesn't have enabled, or a premium feature your plan doesn't include.
**How to fix it:** Remove the rule, or enable the feature in the package settings.
## Authentication errors (407)
These errors mean SOAX couldn't verify your identity. Your request never reached a proxy node.
### ERR\_AUTH\_REQUIRED
```
HTTP/1.1 407 Proxy Authentication Required
X-SOAX-Error: ERR_AUTH_REQUIRED
X-SOAX-Detail: No credentials supplied
```
**What it means:** No credentials were sent. Either you didn't pass a username/password, or you're trying IP Auth from an IP that isn't on the allowlist.
**How to fix it:**
* For username/password: make sure your client is actually sending the proxy username and password. Some HTTP clients need explicit configuration.
* For IP Auth: check your outbound IP at [checker.soax.com/api/ipinfo](https://checker.soax.com/api/ipinfo) without a proxy and add it to the package's IP allowlist.
### AUTH\_FAILED
```
HTTP/1.1 407 Proxy Authentication Required
X-SOAX-Error: AUTH_FAILED
X-SOAX-Detail: Invalid or missing package key
```
**What it means:** Your package key is wrong, missing, or expired.
**How to fix it:**
* Check that your package key matches what's in the dashboard. Copy it exactly.
* Make sure the key is in the password field, not the username field. The format is `{rules}:{package_key}`, separated by a colon.
* If you recently regenerated your package key, update it everywhere you use it. The old key stops working immediately.
### IP\_NOT\_ALLOWED
```
HTTP/1.1 407 Proxy Authentication Required
X-SOAX-Error: IP_NOT_ALLOWED
X-SOAX-Detail: Client IP not in allowlist
```
**What it means:** You're using IP Auth, but the IP address you're connecting from isn't on the allowlist for this package.
**How to fix it:**
* In the dashboard, go to your proxy package settings and check the IP Allowlist.
* Add the public IP of the machine making the request. This is the IP the internet sees, not your local/private IP. You can check your public IP at [checker.soax.com/api/ipinfo](https://checker.soax.com/api/ipinfo) without a proxy.
* If you're behind a corporate NAT or VPN, your outbound IP may be different from what you expect. Add the correct outbound IP.
* If you don't need IP Auth, switch to username/password authentication instead.
### PACKAGE\_SUSPENDED
```
HTTP/1.1 407 Proxy Authentication Required
X-SOAX-Error: PACKAGE_SUSPENDED
X-SOAX-Detail: Account suspended or disabled
```
**What it means:** Your account or proxy package has been suspended.
**How to fix it:**
* Check the dashboard for any account notifications or alerts.
* This can happen if your subscription has expired, your payment method failed, or your account was flagged for a policy violation.
* Contact support if you believe this is an error.
### PACKAGE\_PAUSED
```
HTTP/1.1 407 Proxy Authentication Required
X-SOAX-Error: PACKAGE_PAUSED
X-SOAX-Detail: Package is paused
```
**What it means:** The package itself has been paused from the dashboard. This is reversible — pausing keeps settings intact but rejects all requests.
**How to fix it:** Resume the package from the dashboard, or use a different active package.
### PACKAGE\_NO\_CREDITS
```
HTTP/1.1 407 Proxy Authentication Required
X-SOAX-Error: PACKAGE_NO_CREDITS
X-SOAX-Detail: Package is out of credits or quota
```
**What it means:** Your organization's shared credit pool is empty, so there are no credits left to spend on this request. This is account-wide — it affects every package in the organization, not just this one.
This is different from `429 TRAFFIC_LIMIT_EXCEEDED`, which is returned when a single package hits its own GB cap while the organization still has credits. `PACKAGE_NO_CREDITS` means the organization has run out of credits entirely.
**How to fix it:**
* Top up your organization's credits in the dashboard. Only organization Owners can purchase credits.
* Set up auto top-up under [**Settings → Credits**](https://platform.soax.com/settings/credits) to avoid this happening again.
## Session errors (409)
### SESSION\_PARAMS\_MISMATCH
```
HTTP/1.1 409 Conflict
X-SOAX-Error: SESSION_PARAMS_MISMATCH
X-SOAX-Detail: Session ID reused with different rules
```
**What it means:** You sent a request with a session ID that already exists, but the rules don't match what the session was created with. Rules are locked on first use — you can't change them by sending different rules on a later request.
**How to fix it:**
* Use a new session ID if you want different rules.
* Keep the rules identical every time you send the same session ID.
## Rate and quota errors (429)
These errors mean your request was valid but exceeded a limit on your package.
### RATE\_LIMIT\_EXCEEDED
```
HTTP/1.1 429 Too Many Requests
X-SOAX-Error: RATE_LIMIT_EXCEEDED
X-SOAX-Detail: RPS or concurrent connection limit reached
```
**What it means:** You've hit the requests-per-second (RPS) or concurrent connection limit on your package.
**How to fix it:**
* Reduce the number of parallel requests you're sending.
* If you're using a worker pool, lower the concurrency.
* Check your package limits in the dashboard. On the Sandbox plan, the concurrent connections limit is 4,000 per proxy gateway.
* If you consistently need more throughput, contact support — limits can be evaluated and increased.
### TRAFFIC\_LIMIT\_EXCEEDED
```
HTTP/1.1 429 Too Many Requests
X-SOAX-Error: TRAFFIC_LIMIT_EXCEEDED
X-SOAX-Detail: Traffic limit exceeded
```
**What it means:** This package has a per-package traffic limit (in GB) and you've used it all. The cap is set by an organization Owner and applies only to this package — your organization may still have plenty of credits; this package just isn't allowed to consume more.
This is different from `407 PACKAGE_NO_CREDITS`, which means the organization's entire credit pool is empty. `TRAFFIC_LIMIT_EXCEEDED` is a cap on one package, not an account-wide credit shortage.
**How to fix it:**
* Check this package's usage in the dashboard under [Usage & Analytics](https://platform.soax.com/usage).
* Ask an organization Owner to raise or remove the package's traffic limit.
* If you also see `407 PACKAGE_NO_CREDITS`, the organization is out of credits — top up to continue.
## Node errors (502)
These errors mean SOAX accepted your request but couldn't complete it through the proxy network.
### NODE\_UNAVAILABLE
```
HTTP/1.1 502 Bad Gateway
X-SOAX-Error: NODE_UNAVAILABLE
X-SOAX-Detail: Node went offline or connection refused
```
**What it means:** The node assigned to your request went offline or refused the connection.
**How to fix it:**
* If you're using rotating mode (no session), just retry. The next request will get a different node.
* If you're using a session, your `onerror` rule controls what happens next. The default (`onerror-replace`) automatically gets a new node. If you set `onerror-fail`, you'll see this error instead of an automatic replacement.
* If this happens frequently for a specific geo target, the available pool in that region may be limited. Try broadening your filters (e.g. target the country instead of a specific city).
### NODE\_NOT\_FOUND
```
HTTP/1.1 502 Bad Gateway
X-SOAX-Error: NODE_NOT_FOUND
X-SOAX-Detail: No nodes matching country=us, city=los_angeles, network=mob
```
**What it means:** No nodes in the pool match your filtering rules right now.
**How to fix it:**
* Check the `X-SOAX-Detail` header for exactly which filters couldn't be matched.
* Broaden your targeting. The most common cause is combining a small city with a specific ISP or carrier, leaving very few eligible nodes.
* If you're targeting mobile (`network-mob`), remember the mobile pool is smaller than residential. Try `network-res` or `network-any` to see if the issue is mobile-specific.
* Some country/city/ISP combinations may have limited availability at certain times of day. Try again later or target a nearby city instead.
### RETRIES\_EXHAUSTED
```
HTTP/1.1 502 Bad Gateway
X-SOAX-Error: RETRIES_EXHAUSTED
X-SOAX-Detail: All onerror-retry_N attempts failed, replacement also failed
```
**What it means:** All retries failed and the replacement node also failed. With `onerror-retry_N`, SOAX retries on the current node up to N times. If all of those fail, it then tries to replace the node — and that replacement attempt also failed.
**How to fix it:**
* This usually means the pool matching your filters is under heavy load or very small. Broaden your geo or ISP filters.
* If you're using `onerror-retry_N` with a high retry count, consider reducing it. Retrying many times on a failing node wastes time.
* Check if the issue is transient by retrying the request after a short delay.
## Binding errors (503)
### BOUND\_NODE\_FAILED
```
HTTP/1.1 503 Service Unavailable
X-SOAX-Error: BOUND_NODE_FAILED
X-SOAX-Detail: bind-node session and node has failed, no replacement allowed
```
**What it means:** You used `bind-node` to lock your session to a specific node, and that node is no longer available. Because `bind-node` prevents automatic replacement, the request fails.
**How to fix it:**
* This is expected behavior with `bind-node`. The tradeoff for strict node binding is that you lose the safety net of automatic replacement.
* Start a new session (use a different session ID) to get assigned to a new node.
* If you want retries on the same node before failing, combine `bind-node` with `onerror-retry_N`. For example: `session-x-bind-node-onerror-retry_3`.
* If you don't need strict node binding, remove `bind-node` from your rules and let the system handle replacement automatically.
## Protocol errors (403)
### DISALLOWED
```
HTTP/1.1 403 Forbidden
X-SOAX-Error: DISALLOWED
X-SOAX-Detail: Protocol not in allowed list
```
**What it means:** The protocol you used isn't permitted on this package.
**How to fix it:**
* Switch to an allowed protocol (HTTP, HTTPS, or SOCKS5).
* If you need a specific protocol that's currently blocked, update the package settings in the dashboard.
## Timeout errors (504)
### GATEWAY\_TIMEOUT
```
HTTP/1.1 504 Gateway Timeout
X-SOAX-Error: GATEWAY_TIMEOUT
X-SOAX-Detail: Request timed out after tunnel established
```
**What it means:** The proxy connection was established successfully, but the request to the target website timed out. This could be the node being slow or the target website being unresponsive.
**How to fix it:**
* Check if the target website is accessible directly (without a proxy). If it's down, the proxy can't help.
* If the target is a large page or slow API, increase the timeout on your HTTP client.
* Try a different geo target. Some regions have higher latency nodes than others.
* If this happens consistently on specific targets, the target may be rate-limiting or blocking the exit IP at the TCP level. Try rotating to a fresh IP.
## Quick reference
| Code | X-SOAX-Error | Category | Cause |
| ---- | ------------------------- | ---------- | ---------------------------------------------------- |
| 400 | `ERR_INVALID_VALUE` | Validation | Bad value for a rule (country code, ISP, ASN, etc.) |
| 400 | `ERR_MISSING_PARAMS` | Validation | No targeting rule supplied |
| 400 | `ERR_UNKNOWN_PARAM` | Validation | Unrecognised rule name |
| 400 | `ERR_DUPLICATE_PARAM` | Validation | Same rule appears twice |
| 400 | `ERR_INVALID_COMBINATION` | Validation | Rules that can't be used together |
| 400 | `ERR_PARAM_NOT_ALLOWED` | Validation | Rule not permitted on this package or context |
| 403 | `DISALLOWED` | Protocol | Protocol not in the allowed list |
| 407 | `ERR_AUTH_REQUIRED` | Auth | No credentials supplied |
| 407 | `AUTH_FAILED` | Auth | Wrong or missing package key |
| 407 | `IP_NOT_ALLOWED` | Auth | Client IP not on allowlist |
| 407 | `PACKAGE_SUSPENDED` | Account | Account suspended |
| 407 | `PACKAGE_PAUSED` | Account | Package paused |
| 407 | `PACKAGE_NO_CREDITS` | Account | Organization credit pool empty (account-wide) |
| 409 | `SESSION_PARAMS_MISMATCH` | Session | Session ID reused with different rules |
| 429 | `RATE_LIMIT_EXCEEDED` | Limit | RPS or connection limit hit |
| 429 | `TRAFFIC_LIMIT_EXCEEDED` | Limit | Package's own GB cap reached (org still has credits) |
| 502 | `NODE_UNAVAILABLE` | Node | Node went offline |
| 502 | `NODE_NOT_FOUND` | Node | No nodes match your filters |
| 502 | `RETRIES_EXHAUSTED` | Node | All retries and replacement failed |
| 503 | `BOUND_NODE_FAILED` | Binding | Bound node failed, no replacement allowed |
| 504 | `GATEWAY_TIMEOUT` | Timeout | Request timed out after tunnel established |
## Still stuck?
If you've worked through the fixes above and the issue persists, check the [Connection Debugging](/troubleshooting/connection-debugging) page for a step-by-step diagnostic checklist, or contact support through the dashboard.
## Next steps
Step-by-step checklist for diagnosing connection issues.
Answers to the most common questions.
Full rule reference including error handling options.
Make sure your credentials are set up correctly.
# FAQ
Source: https://developers.soax.com/troubleshooting/faq
Answers to the most common questions about SOAX. Find what you need here before reaching out to support.
## Getting started
### Do you offer a free trial?
There's no separate trial — when you sign up, your account starts on the **Sandbox plan** automatically. Sandbox gives you access to residential and mobile proxies right away, with no time limit.
To start making requests, add credits to your balance. The minimum top-up is **\$25**.
Sandbox has higher per-GB rates than paid plans and is limited to 2 packages and 1 seat, but it's enough to test your integration before committing to a plan.
***
### How do credits work?
Credits are deducted based on the GB of traffic your proxy requests consume. The rate depends on your plan and which country tier the traffic routes through.
Credits are added to your prepaid credit balance in separate blocks. Each block has its own expiry date, which does not change.
Credits added on Sandbox or a monthly plan expire 60 days after they are added. Credits added on an annual plan expire 12 months after they are added.
Renewing, changing, or cancelling your plan does not change the expiry dates of credits already in your balance. If credits are available in several unexpired blocks, we use the block that expires first.
Credits cover proxy traffic only. They can't be used to pay for subscription renewal — your plan renews via your saved payment method.
***
### Which countries are in Tier 1, 2, and 3?
Tiers reflect the cost of sourcing IPs in each region. Tier 1 is the most expensive per GB; Tier 3 is the cheapest.
Check which tier your target countries fall into before building your targeting logic. Routing through Tier 1 instead of Tier 3 can cost up to 8× more per GB on the same plan.
Australia, Austria, Belgium, Canada, China, Cyprus, Czech Republic, Denmark, Finland, France, Germany, Greece, Hong Kong, Ireland, Italy, Japan, Korea, Latvia, Lithuania, Luxembourg, Netherlands, New Zealand, Norway, Poland, Portugal, Slovakia, Spain, Sweden, Switzerland, Taiwan, United Kingdom, United States.
Albania, Algeria, Argentina, Armenia, Azerbaijan, Bahrain, Belarus, Bolivia, Bosnia and Herzegovina, Brazil, Bulgaria, Chile, Colombia, Costa Rica, Croatia, Dominican Republic, Egypt, El Salvador, Estonia, Georgia, Guatemala, Guyana, Honduras, Hungary, India, Indonesia, Iran, Israel, Jamaica, Jordan, Kazakhstan, Kuwait, Kyrgyzstan, Malaysia, Mauritius, Mexico, Moldova, Mongolia, Morocco, Nigeria, Pakistan, Panama, Peru, Qatar, Romania, Russia, Saudi Arabia, Serbia, Singapore, South Africa, Sri Lanka, Tajikistan, Thailand, Tunisia, Turkey, Turkmenistan, UAE, Ukraine, Uruguay, Uzbekistan, Viet Nam, Zimbabwe.
Afghanistan, Aland Islands, American Samoa, Andorra, Angola, Anguilla, Antigua and Barbuda, Aruba, Bahamas, Bangladesh, Barbados, Belize, Benin, Bermuda, Bhutan, Bonaire Sint Eustatius and Saba, Botswana, Brunei Darussalam, Burkina Faso, Burundi, Cabo Verde, Cambodia, Cameroon, Cayman Islands, Central African Republic, Chad, Comoros, Congo, Congo The Democratic Republic of, Cook Islands, Cote D'ivoire, Cuba, Curacao, Djibouti, Dominica, Ecuador, Equatorial Guinea, Ethiopia, Faroe Islands, Fiji, French Guiana, French Polynesia, Gabon, Gambia, Ghana, Gibraltar, Greenland, Grenada, Guadeloupe, Guam, Guernsey, Guinea, Guinea-Bissau, Haiti, Iceland, Iraq, Isle of Man, Jersey, Kenya, Kiribati, Lebanon, Lesotho, Liberia, Libya, Macao, Madagascar, Malawi, Maldives, Mali, Malta, Marshall Islands, Martinique, Mauritania, Mayotte, Micronesia Federated States of, Monaco, Montenegro, Montserrat, Mozambique, Myanmar, Namibia, Nauru, Nepal, New Caledonia, Nicaragua, Niger, Northern Mariana Islands, Oman, Palau, Palestine State of, Papua New Guinea, Paraguay, Philippines, Puerto Rico, Reunion, Rwanda, Saint Barthelemy, Saint Kitts and Nevis, Saint Lucia, Saint Martin French Part, Saint Pierre and Miquelon, Saint Vincent and The Grenadines, Samoa, San Marino, Sao Tome and Principe, Senegal, Seychelles, Sierra Leone, Sint Maarten Dutch Part, Slovenia, Solomon Islands, Somalia, South Sudan, Sudan, Suriname, Swaziland, Syrian Arab Republic, Tanzania United Republic of, Timor-Leste, Togo, Tonga, Trinidad and Tobago, Turks and Caicos Islands, Uganda, Vanuatu, Venezuela Bolivarian Republic of, Virgin Islands British, Virgin Islands U.S., Yemen, Zambia.
***
### Does SOAX work with browser automation and anti-detect browsers?
Yes. SOAX uses standard proxy protocols (HTTP, HTTPS, SOCKS5), so it works with any tool that supports proxy connections.
***
## Payments and billing
### How do I update my billing address or company details?
Go to [**Settings → Billing**](https://platform.soax.com/settings/billing) → **Edit billing info**. You can update your company name, billing address, and VAT ID there.
***
## Targeting and sessions
### What's the difference between rotating and session-based connections?
Rotating (no `session` parameter) gives you a fresh IP on every request. Adding a session ID (`session-yourname`) keeps the same IP across requests. Ephemeral sessions expire after 60 seconds of inactivity. Session IDs are letters, digits, and underscores, up to 32 characters.
See [Residential proxies](/proxies/residential) and [Mobile proxies](/proxies/mobile) for the full reference, including rotation and error handling rules.
***
### How do I target a specific country, city, or ISP?
Add rules to your connection string separated by hyphens. Multi-word values use underscores:
```text theme={null}
country-us-city-new_york-isp-comcast:pk_abc123@proxy.soax.com:1337
```
Available ISP names are listed in your package settings in the dashboard.
See [Authentication](/getting-started/authentication) for the full connection string format and examples.
***
### Is ZIP-code targeting supported?
Yes, on **Scale and Enterprise plans** only. Lower plans support targeting down to country, region, city, and ISP.
***
## Troubleshooting
### My traffic was consumed faster than expected. Why?
Most likely causes: automatic retries consuming traffic on failed requests, large responses including images and scripts, high concurrency, or traffic routing through Tier 1 countries when Tier 3 would work. Check [**Usage breakdown**](https://platform.soax.com/usage) for a breakdown by package and time period.
***
### Are there any request rate limits?
Each package has two limits: a requests-per-second (RPS) limit and a concurrent connections limit. Both are enforced together. On the Sandbox plan, the concurrent connections limit is **4,000 per proxy gateway**. There's also a customer-level ceiling across all your packages, so creating more packages doesn't sidestep the limit.
If you hit either limit, you'll get a `429` error with [`X-SOAX-Error: RATE_LIMIT_EXCEEDED`](/troubleshooting/error-codes#rate_limit_exceeded).
If your use case needs more headroom, ask an organization Owner to raise the package limit. The plan-level ceiling across all packages is tied to your plan — an Owner can contact support to have the limits evaluated and increased.
***
## Account & compliance
### Can I change my email address?
Yes. Go to [**Settings → Profile → Authentication**](https://platform.soax.com/settings/profile) and update your email. You'll get a confirmation code at the new address — the change takes effect once you confirm it.
If you've completed identity verification, you'll need to go through the verification process again after changing your email.
***
### When is identity verification required?
You only need to verify your identity if you want to pay with cryptocurrency or need access to restricted domains or ports.
Verification is handled by [Sumsub](https://sumsub.com/), a third-party KYC provider. The process takes around 5 minutes. SOAX doesn't have access to your documents — we only see the final verification status.
To start, go to [**Settings → Profile → Verification**](https://platform.soax.com/settings/profile).
***
### What counts as acceptable use?
You may use SOAX only for lawful purposes and in accordance with our [Terms of use](https://soax.com/legal/terms).
You may not use the Services to:
* Violate any applicable laws or regulations
* Infringe upon the intellectual property rights of others
* Transmit malicious code or interfere with the Services
* Collect personal data without proper authorization
* Engage in any activity that could damage SOAX's reputation or business
If you have questions about whether your use case is covered, email [legal@soax.com](mailto:legal@soax.com).
***
### How do I request access to a restricted domain or port?
Some domains (financial services, government, certain news sites) and ports (including SMTP 25, 465, 587) are restricted by default. To request access:
1. Complete identity verification — go to [**Settings → Profile → Verification**](https://platform.soax.com/settings/profile).
2. Submit the request through your package's **Guardrails** tab — open the package in [**Packages**](https://platform.soax.com/packages), then go to **Settings → Guardrails → Access exceptions → Request exception** — with details of the domain or port you need access to.
Only organization **Owners** can request access to restricted domains or ports. If you're a Member, ask an Owner on your team. See [Team and permissions](/dashboard/account-billing#team-and-permissions).
***
### Does SOAX support UDP?
Yes. Residential and Mobile proxies support UDP.
UDP works over **SOCKS5** only. Make sure your tool or library supports SOCKS5 UDP — not all of them do.
***
## Still have questions?
* Live chat — available in the dashboard (bottom right corner)
* Email — [support@soax.com](mailto:support@soax.com)
* [Error codes](/troubleshooting/error-codes)
* [Connection debugging](/troubleshooting/connection-debugging)
## Next steps
Make your first proxy request in under 5 minutes.
Understand packages, sessions, rules, and bindings.
Full list of errors with causes and fixes.
Step-by-step checklist for connection issues.
# IP Quality & Fraud Score
Source: https://developers.soax.com/troubleshooting/ip-quality-fraud-score
What fraud score is, why it matters for proxy use, how different proxy types compare, and how SOAX maintains pool quality.
Fraud score is one of the most misunderstood concepts in proxy infrastructure. This page explains what it is, what affects it, and what it means for your use case.
## What is a fraud score?
Fraud score is a reputation metric that anti-fraud systems assign to an IP address. It reflects how likely that IP is to be associated with malicious or automated activity — spam, credential stuffing, ad fraud, scraping, and so on.
Several services maintain these scores: IPQualityScore, Scamalytics, IPInfo, MaxMind, and others. Websites and platforms use them (often without telling you) to decide whether to serve content, trigger a CAPTCHA, flag an account, or block a request entirely.
A low score means the IP looks clean. A high score means it's been flagged — either because it was previously used for something suspicious, or because it belongs to a subnet with a bad reputation.
## How proxy types compare
Not all IPs are treated equally by fraud scoring systems. The IP's origin is a major factor.
**Residential IPs** come from real home connections assigned by ISPs to real subscribers. Fraud systems treat them as legitimate user traffic by default. They have the lowest fraud scores of any proxy type — unless a specific IP has been abused previously.
**Mobile IPs** come from carrier connections. Because carriers use NAT to share a single IP across hundreds of real users, fraud systems are reluctant to flag mobile IPs aggressively — blocking one could mean blocking thousands of legitimate users. Mobile IPs tend to have very low fraud scores.
**ISP proxies** (static residential) are hosted in datacenters but registered to ISPs rather than cloud providers. They score better than datacenter IPs but not as well as genuine residential connections.
**Datacenter IPs** are the easiest to identify. ASN lookups immediately reveal the hosting provider (AWS, GCP, DigitalOcean, etc.). Most fraud scoring systems assign datacenter IPs a high baseline score regardless of their history. This doesn't mean they're useless — many targets don't check fraud scores — but for anything that does, datacenter IPs are the most likely to be blocked.
## What else affects an IP's fraud score
**Usage history.** If an IP was previously used for spam or credential stuffing, that history follows it. This is why IP pool quality matters — a provider that doesn't monitor for abuse will accumulate IPs with bad histories.
**Subnet reputation.** Fraud systems don't just look at individual IPs — they look at the entire subnet. If many IPs in a /24 block have been flagged, all IPs in that range get a higher baseline score.
**IP age and stability.** IPs that have been assigned to the same subscriber for a long time tend to score better than freshly allocated IPs. Residential IPs are more likely to have stable histories.
**Concurrent usage patterns.** An IP that's handling hundreds of requests per minute looks different from a typical home connection. Some fraud systems factor in behavioral signals in addition to static reputation.
## How SOAX maintains pool quality
SOAX actively monitors the proxy pool and removes nodes that show signs of degraded reputation. This includes:
* Checking IPs against major fraud scoring databases.
* Monitoring for abuse patterns within the network.
* Removing nodes that are generating unusual error rates or getting blocked at abnormally high rates.
This is ongoing work, not a one-time check. The pool changes constantly as new nodes join and underperforming nodes are removed.
That said, no pool is perfect. If you're targeting sites that do aggressive fraud scoring, you may still encounter IPs with elevated scores — especially in smaller geos where the available pool is limited. The practical approach is to treat elevated block rates as a signal to rotate to a fresh IP rather than expecting every IP to be pristine.
## What this means for your setup
**If you're scraping public data at scale:** fraud score matters less. Most scraping targets block based on request patterns, not IP reputation. Focus on rotation strategy and realistic headers.
**If you're doing ad verification or brand protection:** fraud score matters more. The target systems are specifically designed to detect non-human traffic, and they use fraud score as one signal. See [Choosing the right proxy type](/getting-started/choosing-proxy-type) to pick the best fit for your use case.
**If you're working with platforms that create or manage accounts:** fraud score is critical. Platforms that tie accounts to IPs (social media, e-commerce, marketplaces) use fraud scoring to flag suspicious activity.
## Next steps
How proxies affect account-level bans and how to reduce that risk.
What to do when the target site is blocking your requests.
What affects response times and how to optimize for speed.
Compare residential, mobile, ISP, and datacenter proxies.
# Proxy Speed & Latency
Source: https://developers.soax.com/troubleshooting/proxy-speed-latency
What affects proxy latency, what to expect from each proxy type, and how to measure and improve performance in your setup.
Proxy latency is real and worth understanding before you build. This page explains what drives it, what numbers to expect, and what you can do when speed matters.
## What latency means in a proxy setup
When you send a request through SOAX, there are two network hops instead of one:
1. Your application → SOAX proxy node
2. SOAX proxy node → target website
The total response time you see includes both hops plus the target's own response time. SOAX controls the first hop and the quality of the second; the target's response time is outside our control.
When people say "the proxy is slow", it's usually one of three things:
* The proxy node is geographically far from the target.
* The eligible node pool is small due to narrow targeting, and the system spends time finding a match.
* The target itself is slow or is rate-limiting the node.
## What to expect by proxy type
There's a direct tradeoff between how legitimate an IP looks to the target and how fast it is. Residential and mobile IPs come from real consumer devices on real ISP connections — that's why they're harder to detect, and also why they add more latency than datacenter IPs.
| Proxy type | Typical added latency | Notes |
| ------------------------ | --------------------- | ---------------------------------------------------------------------- |
| Residential | 100–800ms | Varies by country and ISP. Home connections can be asymmetric. |
| Mobile | 150–1000ms | Cellular networks add variable overhead. Higher on congested carriers. |
| ISP (static residential) | 50–200ms | Datacenter infrastructure with residential IP registration. |
| Datacenter | 20–100ms | Fastest option, but higher detection risk on protected targets. |
These are added latency figures on top of the target's own response time. A target that responds in 300ms over a direct connection might respond in 600–900ms through a residential proxy. That's expected behavior, not a problem.
## What affects latency in practice
**Geographic distance between the node and the target.** SOAX automatically routes your requests through the nearest infrastructure server — we have servers in Europe, Asia, and North America. This means the first hop (your application → SOAX) is handled efficiently regardless of where you're running your code. The second hop (SOAX → target) still depends on which country your proxy node is in relative to the target's server location — so if speed matters, it's worth matching your node's country to where the target is hosted.
**Pool size.** The more specific your targeting, the smaller the eligible pool. If you're targeting a specific city and ISP, the system may need more time to find an available matching node. [`NODE_NOT_FOUND`](/troubleshooting/error-codes#node_not_found) errors are the extreme case, but even before that, narrow pools can add allocation overhead. Start broad and narrow down only if you need to.
**Concurrency.** High concurrency against a small pool can create contention. If you're sending hundreds of parallel requests targeting a narrow geo, some will wait for an available node. Broaden your targeting or reduce concurrency to keep response times predictable.
**Target site behavior.** Some targets actively slow down traffic they suspect is automated. If latency spikes on a specific target but works fine on others, that's the target responding to your traffic pattern — not a proxy issue. See [CAPTCHA & ban rates](/troubleshooting/captcha-ban-rates).
## How to reduce latency
**Match the node's country to the target's server location.** The content you want to access and the server you're hitting can be in different countries — target the server, not the content. If the target is hosted in Germany, use `country-de`.
**Use sessions for multi-request workflows.** If you're making several requests as part of the same task, bind them to a session. You pay the node allocation cost once and reuse the same node for subsequent requests.
**Broaden your targeting.** Narrow targeting means a smaller pool and higher allocation time. If you're targeting city + ISP, try removing the ISP filter first and see if latency improves.
**Adjust your HTTP client timeout.** Residential and mobile proxies are slower than datacenter IPs. If your client has a tight timeout (e.g. 5 seconds), you'll see false failures on perfectly healthy connections. 15–30 seconds is more appropriate for residential traffic.
## Next steps
Understand what makes an IP trustworthy and how SOAX maintains pool quality.
What to do when the target site is blocking your requests.
Full parameter reference including session and rotation options.
Step-by-step checklist for diagnosing connection issues.