Credentials
Create and manage proxy sub-credentials on a Proxio residential service, list, create (with an optional KB/MB/GB traffic quota), update, delete, and rotate the password. Up to 20 per service.
A credential is a sub-account on a residential service: its own proxy username and password, with an optional bandwidth quota. Use separate credentials to split one package across jobs, teammates, or environments, each with its own whitelist, sessions, and usage.
Scopes: read for GET, write for everything else.
Two endpoints are residential only
Create and rotate-password check the service category and return
UNSUPPORTED_OPERATION on a static
category (ISP, datacenter), because those services ship a fixed proxy list
rather than gateway sub-accounts. See
Services.
The read and edit endpoints do not apply that check. List, get, update, and delete work on a static service's credential rows the same way they do on a residential one.
List credentials
GET /services/{id}/credentials returns the full list (up to 20), no cursor.
curl https://dashboard.proxio.net/api/v1/services/clpkg_2a9x/credentials \
-H "Authorization: Bearer pxo_9fJ2kQ7xR4mN8pL1dW6vB3cH5tZ0aYqS7dK2mN9x"import requests
BASE = "https://dashboard.proxio.net/api/v1"
API_KEY = "pxo_9fJ2kQ7xR4mN8pL1dW6vB3cH5tZ0aYqS7dK2mN9x"
SERVICE_ID = "clpkg_2a9x"
resp = requests.get(
f"{BASE}/services/{SERVICE_ID}/credentials",
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=15,
)
resp.raise_for_status()
print(resp.json()["data"])const BASE = "https://dashboard.proxio.net/api/v1"
const API_KEY = "pxo_9fJ2kQ7xR4mN8pL1dW6vB3cH5tZ0aYqS7dK2mN9x"
const SERVICE_ID = "clpkg_2a9x"
const res = await fetch(`${BASE}/services/${SERVICE_ID}/credentials`, {
headers: { Authorization: `Bearer ${API_KEY}` },
})
console.log((await res.json()).data)200 response:
{
"data": [
{
"id": "clsub_7h2k",
"label": "Primary",
"username": "abc123xyz",
"password": "secretpass",
"is_primary": true,
"is_active": true,
"quota": null,
"used": { "bytes": "0", "bytes_num": 0, "gigabytes": 0 },
"created_at": "2026-07-02T10:00:00.000Z"
}
],
"meta": { "request_id": "req_8Ke2jP4mQ" }
}quota is null for an uncapped credential, or an object
{ "megabytes": <int>, "gigabytes": <number> } when a cap is set (the cap is
stored as whole megabytes). Units are decimal SI: 1 GB = 1000 MB = 1,000,000 KB.
used is a byte quantity.
Create a credential
POST /services/{id}/credentials. Body: label and an optional traffic cap
given in one of three units. Up to 20 credentials per service.
Body
| Field | Type | Notes |
|---|---|---|
label | string | A name to identify the credential. |
quota_mb | integer | null | Traffic cap in MB (positive integer). |
quota_gb | number | null | Traffic cap in GB (positive, decimals allowed, e.g. 1.5). |
quota_kb | integer | null | Traffic cap in KB (positive integer). |
Send exactly one of quota_mb, quota_gb, or quota_kb, or omit all three
for an uncapped credential. The value is converted to whole megabytes (rounded
up, minimum 1 MB) and capped at 10,000,000 MB. Passing an explicit null on any
one of the fields clears the cap.
Providing more than one quota field (even if one is null) fails with
VALIDATION_ERROR and
details: [{ "field": ..., "issue": "conflicting_quota_fields" }]. A value over
the maximum fails with the same code and
details: [{ "field": ..., "issue": "too_large", "max_mb": 10000000 }].
curl -X POST https://dashboard.proxio.net/api/v1/services/clpkg_2a9x/credentials \
-H "Authorization: Bearer pxo_9fJ2kQ7xR4mN8pL1dW6vB3cH5tZ0aYqS7dK2mN9x" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{ "label": "scraper-a", "quota_gb": 10 }'import uuid
import requests
BASE = "https://dashboard.proxio.net/api/v1"
API_KEY = "pxo_9fJ2kQ7xR4mN8pL1dW6vB3cH5tZ0aYqS7dK2mN9x"
SERVICE_ID = "clpkg_2a9x"
resp = requests.post(
f"{BASE}/services/{SERVICE_ID}/credentials",
headers={
"Authorization": f"Bearer {API_KEY}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={"label": "scraper-a", "quota_gb": 10},
timeout=15,
)
resp.raise_for_status()
created = resp.json()["data"]
print(created["username"], created["password"])import { randomUUID } from "node:crypto"
const BASE = "https://dashboard.proxio.net/api/v1"
const API_KEY = "pxo_9fJ2kQ7xR4mN8pL1dW6vB3cH5tZ0aYqS7dK2mN9x"
const SERVICE_ID = "clpkg_2a9x"
const res = await fetch(`${BASE}/services/${SERVICE_ID}/credentials`, {
method: "POST",
headers: {
Authorization: `Bearer ${API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": randomUUID(),
},
body: JSON.stringify({ label: "scraper-a", quota_gb: 10 }),
})
const { data } = await res.json()
console.log(data.username, data.password)201 response:
{
"data": {
"id": "clsub_9m4p",
"label": "scraper-a",
"username": "k7p2q1m9x3ab",
"password": "gk2rt81wq7pz",
"is_primary": false,
"is_active": true,
"quota": { "megabytes": 10000, "gigabytes": 10 },
"used": { "bytes": "0", "bytes_num": 0, "gigabytes": 0 },
"created_at": "2026-07-17T09:30:00.000Z"
},
"meta": { "request_id": "req_8Ke2jP4mQ" }
}Reading the password later
The plaintext password is in this response, and list and get responses
include it too, so any key with read scope can recover it. The one exception
is an idempotent replay of this create,
which returns password: null; re-read the credential instead.
Reaching the cap returns LIMIT_REACHED (409)
with details: [{ "limit": 20 }].
Creating a credential also emits a credential.created
webhook event, if you have an endpoint
subscribed to it.
Get one credential
GET /services/{id}/credentials/{credId} returns a single credential in the
same shape as a list item.
curl https://dashboard.proxio.net/api/v1/services/clpkg_2a9x/credentials/clsub_9m4p \
-H "Authorization: Bearer pxo_9fJ2kQ7xR4mN8pL1dW6vB3cH5tZ0aYqS7dK2mN9x"import requests
BASE = "https://dashboard.proxio.net/api/v1"
API_KEY = "pxo_9fJ2kQ7xR4mN8pL1dW6vB3cH5tZ0aYqS7dK2mN9x"
resp = requests.get(
f"{BASE}/services/clpkg_2a9x/credentials/clsub_9m4p",
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=15,
)
resp.raise_for_status()
print(resp.json()["data"])const BASE = "https://dashboard.proxio.net/api/v1"
const API_KEY = "pxo_9fJ2kQ7xR4mN8pL1dW6vB3cH5tZ0aYqS7dK2mN9x"
const res = await fetch(
`${BASE}/services/clpkg_2a9x/credentials/clsub_9m4p`,
{ headers: { Authorization: `Bearer ${API_KEY}` } },
)
console.log((await res.json()).data)Update a credential
PATCH /services/{id}/credentials/{credId}. Send any of label, is_active,
and a single quota field (quota_mb, quota_gb, or quota_kb). A quota value
sets a new cap, and an explicit null removes it; the same one-field-only,
whole-MB, and 10,000,000 MB rules as create apply. Omit
the quota fields to leave the current cap unchanged.
curl -X PATCH https://dashboard.proxio.net/api/v1/services/clpkg_2a9x/credentials/clsub_9m4p \
-H "Authorization: Bearer pxo_9fJ2kQ7xR4mN8pL1dW6vB3cH5tZ0aYqS7dK2mN9x" \
-H "Content-Type: application/json" \
-d '{ "label": "scraper-a-eu", "quota_gb": null, "is_active": true }'import requests
BASE = "https://dashboard.proxio.net/api/v1"
API_KEY = "pxo_9fJ2kQ7xR4mN8pL1dW6vB3cH5tZ0aYqS7dK2mN9x"
resp = requests.patch(
f"{BASE}/services/clpkg_2a9x/credentials/clsub_9m4p",
headers={"Authorization": f"Bearer {API_KEY}"},
json={"label": "scraper-a-eu", "quota_gb": None, "is_active": True},
timeout=15,
)
resp.raise_for_status()
print(resp.json()["data"])const BASE = "https://dashboard.proxio.net/api/v1"
const API_KEY = "pxo_9fJ2kQ7xR4mN8pL1dW6vB3cH5tZ0aYqS7dK2mN9x"
const res = await fetch(
`${BASE}/services/clpkg_2a9x/credentials/clsub_9m4p`,
{
method: "PATCH",
headers: {
Authorization: `Bearer ${API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ label: "scraper-a-eu", quota_gb: null, is_active: true }),
},
)
console.log((await res.json()).data)Delete a credential
DELETE /services/{id}/credentials/{credId} returns 204 No Content. The
primary credential (the first one created) can't be deleted, attempting it
returns UNSUPPORTED_OPERATION (400).
curl -X DELETE https://dashboard.proxio.net/api/v1/services/clpkg_2a9x/credentials/clsub_9m4p \
-H "Authorization: Bearer pxo_9fJ2kQ7xR4mN8pL1dW6vB3cH5tZ0aYqS7dK2mN9x"import requests
BASE = "https://dashboard.proxio.net/api/v1"
API_KEY = "pxo_9fJ2kQ7xR4mN8pL1dW6vB3cH5tZ0aYqS7dK2mN9x"
resp = requests.delete(
f"{BASE}/services/clpkg_2a9x/credentials/clsub_9m4p",
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=15,
)
print(resp.status_code) # 204const BASE = "https://dashboard.proxio.net/api/v1"
const API_KEY = "pxo_9fJ2kQ7xR4mN8pL1dW6vB3cH5tZ0aYqS7dK2mN9x"
const res = await fetch(
`${BASE}/services/clpkg_2a9x/credentials/clsub_9m4p`,
{ method: "DELETE", headers: { Authorization: `Bearer ${API_KEY}` } },
)
console.log(res.status) // 204Rotate the password
POST /services/{id}/credentials/{credId}/rotate-password generates a new
password and returns it once.
curl -X POST \
https://dashboard.proxio.net/api/v1/services/clpkg_2a9x/credentials/clsub_9m4p/rotate-password \
-H "Authorization: Bearer pxo_9fJ2kQ7xR4mN8pL1dW6vB3cH5tZ0aYqS7dK2mN9x" \
-H "Idempotency-Key: $(uuidgen)"import uuid
import requests
BASE = "https://dashboard.proxio.net/api/v1"
API_KEY = "pxo_9fJ2kQ7xR4mN8pL1dW6vB3cH5tZ0aYqS7dK2mN9x"
resp = requests.post(
f"{BASE}/services/clpkg_2a9x/credentials/clsub_9m4p/rotate-password",
headers={
"Authorization": f"Bearer {API_KEY}",
"Idempotency-Key": str(uuid.uuid4()),
},
timeout=15,
)
resp.raise_for_status()
print(resp.json()["data"]["password"]) # the new passwordimport { randomUUID } from "node:crypto"
const BASE = "https://dashboard.proxio.net/api/v1"
const API_KEY = "pxo_9fJ2kQ7xR4mN8pL1dW6vB3cH5tZ0aYqS7dK2mN9x"
const res = await fetch(
`${BASE}/services/clpkg_2a9x/credentials/clsub_9m4p/rotate-password`,
{
method: "POST",
headers: {
Authorization: `Bearer ${API_KEY}`,
"Idempotency-Key": randomUUID(),
},
},
)
console.log((await res.json()).data.password) // the new password200 response:
{
"data": {
"id": "clsub_9m4p",
"username": "abc123xyz",
"password": "w4hn92xcv5qm",
"propagation_seconds": 30,
"warning": "The previous password keeps working for up to ~30s."
},
"meta": { "request_id": "req_8Ke2jP4mQ" }
}Rotation isn't instant
The new password works immediately, and the old one keeps authenticating for
roughly 30 seconds (propagation_seconds). Expect a brief overlap window, and
don't rely on the old password being rejected the instant you rotate.
Rotating also emits a credential.rotated
webhook event. The new password is
never included in it, a webhook body is a copy whose destination you
don't control, so a subscriber learns that the password changed, not what it
changed to.
Related pages
Usage
Read bandwidth usage for a Proxio service. GET /services/{id}/usage returns a summary (limit, used, remaining, today, success rate) and GET /services/{id}/usage/series returns a zero-filled time series with close-reason breakdowns. Explains the byte-quantity object.
Proxy List
Generate ready-to-use Proxio proxy lines with GET /services/{id}/proxy-list. Targeting, including ASN, is embedded in the username, output as txt, json, or csv, with the full username grammar, every query parameter (count, sesstime, retry, retry_rotate, session_id, asn), sticky vs rotating, and practical recipes.

