Sub-Users API

Sub-users are the individual proxy accounts under your ProxyHat account. Each one carries its own connection credentials and its own traffic limit carved out of your shared account pool. Use these endpoints to list, create, update, delete, and bulk-manage sub-users. Once a sub-user exists, build a ready-to-use connection string for it with the Proxy Descriptors API.

Traffic limits are byte counts. The traffic_limit field is an integer number of bytes, not a human string like "5GB". A value of 0 means the sub-user is uncapped and may draw from the entire remaining account pool. ProxyHat counts traffic in decimal units — 1 GB = 1 000 000 000 bytes — so 5 GB is 5000000000 (5 × 1000³), not 5 × 1024³.

List Sub-Users

GET /v1/sub-users Requires Auth

List Sub-Users

List Sub-Users

Retrieve all of your sub-users (active, suspended, and pending-deletion). Each item is enriched with its current traffic usage, and the meta object carries account-level traffic accounting. Pass ?uuid= to fetch a single sub-user — there is no dedicated single-resource GET endpoint.

No input needed — runs live with your key
Query Parameters
Name Type Required Description
uuid string (uuid) Optional Filter to a single sub-user by UUID. Returns a one-element payload array (or an empty array if it is not yours).
curl https://api.proxyhat.com/v1/sub-users \
  -H "Authorization: Bearer __API_KEY__" \
  -H "Accept: application/json"
import requests

response = requests.get(
    "https://api.proxyhat.com/v1/sub-users",
    headers={
        "Authorization": "Bearer __API_KEY__",
        "Accept": "application/json",
    },
)

body = response.json()
for su in body["payload"]:
    print(su["name"], su["used_traffic"], "/", su["traffic_limit"], "bytes")
const response = await fetch("https://api.proxyhat.com/v1/sub-users", {
  headers: {
    "Authorization": "Bearer __API_KEY__",
    "Accept": "application/json",
  },
});

const body = await response.json();
body.payload.forEach(su =>
  console.log(su.name, su.used_traffic, "/", su.traffic_limit, "bytes")
);
req, _ := http.NewRequest("GET", "https://api.proxyhat.com/v1/sub-users", nil)
req.Header.Set("Authorization", "Bearer __API_KEY__")
req.Header.Set("Accept", "application/json")

resp, err := http.DefaultClient.Do(req)
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()

var body struct {
    Payload []map[string]interface{} `json:"payload"`
}
json.NewDecoder(resp.Body).Decode(&body)
for _, su := range body.Payload {
    fmt.Printf("%v: %v / %v bytes\n", su["name"], su["used_traffic"], su["traffic_limit"])
}
Field notes.
  • lifecycle_status is one of active, suspended, deleting, or deleted — all lowercase. The list only returns active, suspended, and deleting rows.
  • used_traffic and traffic_limit are in bytes. stats_used_traffic is the last-24h usage pulled from the same analytics source as the Analytics API (bytes).
  • proxy_password is returned in the clear for active sub-users so you can build a connection string, but is masked to null once a sub-user is suspended, deleting, or deleted.
  • provisioning is true during the brief window after creation while the proxy is being registered with the upstream provider — the credentials 401 until it flips to false.
  • meta reports account-level accounting in bytes: traffic_total_remaining (your whole pool), traffic_reserved (sum of unused headroom across capped sub-users), and traffic_available (what is left to hand out to new or expanded sub-users).

Create Sub-User

POST /v1/sub-users Requires Auth

Create Sub-User

Create Sub-User

Create a new sub-user. Only proxy_password is required. Returns the created sub-user in the standard envelope with HTTP 200 (not 201). If two-factor auth is enabled on your account, include twofa_code.

Example request: name proxy_password traffic_limit
Request Body
Name Type Required Description
proxy_password string Required Connection password for this sub-user. 9–40 characters, alphanumeric only (must match ^[a-zA-Z0-9]*$).
traffic_limit integer Optional Traffic cap in BYTES. Minimum 0. Omit or pass 0 to leave the sub-user uncapped (draws from the whole account pool).
name string Optional Human-friendly label. Max 40 characters.
notes string Optional Free-form internal notes. Max 5000 characters.
sub_user_group_id string (uuid) Optional Assign the sub-user to one of your sub-user groups. Must be a group you own.
twofa_code string Optional Current 2FA code, required only if two-factor auth is enabled on your account. Max 16 characters.
curl -X POST https://api.proxyhat.com/v1/sub-users \
  -H "Authorization: Bearer __API_KEY__" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "proxy_password": "MyProxyPass123",
    "name": "scraper-01",
    "traffic_limit": 5000000000
  }'
import requests

response = requests.post(
    "https://api.proxyhat.com/v1/sub-users",
    headers={
        "Authorization": "Bearer __API_KEY__",
        "Accept": "application/json",
    },
    json={
        "proxy_password": "MyProxyPass123",
        "name": "scraper-01",
        "traffic_limit": 5000000000,  # 5 GB in bytes
    },
)

sub_user = response.json()["payload"]
print(sub_user["uuid"], sub_user["proxy_username"])
const response = await fetch("https://api.proxyhat.com/v1/sub-users", {
  method: "POST",
  headers: {
    "Authorization": "Bearer __API_KEY__",
    "Content-Type": "application/json",
    "Accept": "application/json",
  },
  body: JSON.stringify({
    proxy_password: "MyProxyPass123",
    name: "scraper-01",
    traffic_limit: 5000000000, // 5 GB in bytes
  }),
});

const { payload } = await response.json();
console.log(payload.uuid, payload.proxy_username);
payload := map[string]interface{}{
    "proxy_password": "MyProxyPass123",
    "name":           "scraper-01",
    "traffic_limit":  5000000000, // 5 GB in bytes
}
buf, _ := json.Marshal(payload)

req, _ := http.NewRequest("POST", "https://api.proxyhat.com/v1/sub-users", bytes.NewReader(buf))
req.Header.Set("Authorization", "Bearer __API_KEY__")
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json")

resp, err := http.DefaultClient.Do(req)
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()

var body struct {
    Payload map[string]interface{} `json:"payload"`
}
json.NewDecoder(resp.Body).Decode(&body)
fmt.Println(body.Payload["uuid"], body.Payload["proxy_username"])
Topping up your account does not raise an existing sub-user's limit. Buying more traffic credits the shared account pool, but a capped sub-user keeps the same traffic_limit it already had. When you run more than one proxy, the newly purchased traffic simply becomes traffic_available in the pool — the exhausted sub-user stays dead until you explicitly raise its traffic_limit with an update call (or set it to 0 to uncap it). See the Reselling guide for how the pool, reservations, and per-sub-user limits fit together.

Update Sub-User

PUT /v1/sub-users/{sub_user} Requires Auth

Update Sub-User

Update Sub-User

Update a sub-user's password, traffic limit, name, notes, or group. IMPORTANT: the target is identified by the uuid in the JSON BODY — the {sub_user} path segment is ignored, so any placeholder there works. PATCH is also accepted. Returns the updated sub-user in the envelope.

Example request: uuid auto name traffic_limit
Path Parameters
Name Type Required Description
sub_user string Required Ignored. A value is required to form a valid URL, but the sub-user is resolved from the body uuid, not this segment.
Request Body
Name Type Required Description
uuid string (uuid) Required UUID of the sub-user to update. Must exist and belong to you. This — not the path — determines the target.
proxy_password string Optional New connection password. 9–40 characters, alphanumeric only (^[a-zA-Z0-9]*$).
traffic_limit integer Optional New traffic cap in BYTES (min 0; 0 = uncapped). Raise this to revive a sub-user after topping up your pool.
name string Optional New label. Max 40 characters.
notes string Optional New internal notes. Max 5000 characters.
sub_user_group_id string (uuid) Optional Move the sub-user into one of your groups, or null to unassign.
twofa_code string Optional Current 2FA code, required only if two-factor auth is enabled. Max 16 characters.
curl -X PUT https://api.proxyhat.com/v1/sub-users/_ \
  -H "Authorization: Bearer __API_KEY__" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "uuid": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
    "traffic_limit": 10000000000
  }'
import requests

# The URL path segment is ignored; the uuid in the body is what matters.
response = requests.put(
    "https://api.proxyhat.com/v1/sub-users/_",
    headers={
        "Authorization": "Bearer __API_KEY__",
        "Accept": "application/json",
    },
    json={
        "uuid": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
        "traffic_limit": 10000000000,  # raise cap to 10 GB
    },
)

print(response.json()["payload"]["traffic_limit"])
// The URL path segment is ignored; the body uuid selects the target.
const response = await fetch("https://api.proxyhat.com/v1/sub-users/_", {
  method: "PUT",
  headers: {
    "Authorization": "Bearer __API_KEY__",
    "Content-Type": "application/json",
    "Accept": "application/json",
  },
  body: JSON.stringify({
    uuid: "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
    traffic_limit: 10000000000, // raise cap to 10 GB
  }),
});

const { payload } = await response.json();
console.log(payload.traffic_limit);
payload := map[string]interface{}{
    "uuid":          "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
    "traffic_limit": 10000000000, // raise cap to 10 GB
}
buf, _ := json.Marshal(payload)

// The URL path segment is ignored; the body uuid selects the target.
req, _ := http.NewRequest("PUT", "https://api.proxyhat.com/v1/sub-users/_", bytes.NewReader(buf))
req.Header.Set("Authorization", "Bearer __API_KEY__")
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json")

resp, err := http.DefaultClient.Do(req)
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()

var body struct {
    Payload map[string]interface{} `json:"payload"`
}
json.NewDecoder(resp.Body).Decode(&body)
fmt.Println(body.Payload["traffic_limit"])

Delete Sub-User

DELETE /v1/sub-users/{sub_user} Requires Auth

Delete Sub-User

Delete Sub-User

Delete a sub-user. Like update, the target is taken from the uuid in the JSON BODY — the {sub_user} path segment is ignored. Deletion is asynchronous: the response returns immediately with status "deleting" while the upstream provider account is torn down in the background.

Example request: uuid auto
Path Parameters
Name Type Required Description
sub_user string Required Ignored. Present only to form a valid URL; the target is resolved from the body uuid.
Request Body
Name Type Required Description
uuid string (uuid) Required UUID of the sub-user to delete. Must exist and belong to you.
twofa_code string Optional Current 2FA code, required only if two-factor auth is enabled. Max 16 characters.
curl -X DELETE https://api.proxyhat.com/v1/sub-users/_ \
  -H "Authorization: Bearer __API_KEY__" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "uuid": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"
  }'
import requests

response = requests.delete(
    "https://api.proxyhat.com/v1/sub-users/_",
    headers={
        "Authorization": "Bearer __API_KEY__",
        "Accept": "application/json",
    },
    json={"uuid": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"},
)

print(response.json()["payload"])  # {"deleted": True, "status": "deleting"}
const response = await fetch("https://api.proxyhat.com/v1/sub-users/_", {
  method: "DELETE",
  headers: {
    "Authorization": "Bearer __API_KEY__",
    "Content-Type": "application/json",
    "Accept": "application/json",
  },
  body: JSON.stringify({ uuid: "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d" }),
});

const { payload } = await response.json();
console.log(payload); // { deleted: true, status: "deleting" }
payload := map[string]string{"uuid": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"}
buf, _ := json.Marshal(payload)

req, _ := http.NewRequest("DELETE", "https://api.proxyhat.com/v1/sub-users/_", bytes.NewReader(buf))
req.Header.Set("Authorization", "Bearer __API_KEY__")
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json")

resp, err := http.DefaultClient.Do(req)
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()

var body struct {
    Payload map[string]interface{} `json:"payload"`
}
json.NewDecoder(resp.Body).Decode(&body)
fmt.Println(body.Payload) // map[deleted:true status:deleting]

Reset Usage

POST /v1/sub-users/reset-usage Requires Auth

Reset Usage

Reset Usage

Reset the used_traffic counter back to zero for one or more sub-users. Returns the affected sub-users plus refreshed account traffic stats in meta.

Example request: ids
Request Body
Name Type Required Description
ids array of string (uuid) Required Sub-user UUIDs to reset. At least one required.
curl -X POST https://api.proxyhat.com/v1/sub-users/reset-usage \
  -H "Authorization: Bearer __API_KEY__" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "ids": ["a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"]
  }'
import requests

response = requests.post(
    "https://api.proxyhat.com/v1/sub-users/reset-usage",
    headers={
        "Authorization": "Bearer __API_KEY__",
        "Accept": "application/json",
    },
    json={"ids": ["a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"]},
)

print(response.json()["meta"])
const response = await fetch("https://api.proxyhat.com/v1/sub-users/reset-usage", {
  method: "POST",
  headers: {
    "Authorization": "Bearer __API_KEY__",
    "Content-Type": "application/json",
    "Accept": "application/json",
  },
  body: JSON.stringify({ ids: ["a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"] }),
});

const body = await response.json();
console.log(body.meta);
payload := map[string][]string{"ids": {"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"}}
buf, _ := json.Marshal(payload)

req, _ := http.NewRequest("POST", "https://api.proxyhat.com/v1/sub-users/reset-usage", bytes.NewReader(buf))
req.Header.Set("Authorization", "Bearer __API_KEY__")
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json")

resp, err := http.DefaultClient.Do(req)
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()

var body map[string]interface{}
json.NewDecoder(resp.Body).Decode(&body)
fmt.Println(body["meta"])

Bulk Delete

POST /v1/sub-users/bulk-delete Requires Auth

Bulk Delete

Bulk Delete

Delete several sub-users at once. Like the single delete, teardown is asynchronous. The payload reports exactly which UUIDs were scheduled for deletion, which were skipped (e.g. the default sub-user), and which were not found. Include twofa_code if two-factor auth is enabled.

Example request: ids
Request Body
Name Type Required Description
ids array of string (uuid) Required Sub-user UUIDs to delete. At least one required.
twofa_code string Optional Current 2FA code, required only if two-factor auth is enabled. Max 16 characters.
curl -X POST https://api.proxyhat.com/v1/sub-users/bulk-delete \
  -H "Authorization: Bearer __API_KEY__" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "ids": [
      "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
      "b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e"
    ]
  }'
import requests

response = requests.post(
    "https://api.proxyhat.com/v1/sub-users/bulk-delete",
    headers={
        "Authorization": "Bearer __API_KEY__",
        "Accept": "application/json",
    },
    json={
        "ids": [
            "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
            "b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e",
        ]
    },
)

print(response.json()["payload"]["deleted_count"])
const response = await fetch("https://api.proxyhat.com/v1/sub-users/bulk-delete", {
  method: "POST",
  headers: {
    "Authorization": "Bearer __API_KEY__",
    "Content-Type": "application/json",
    "Accept": "application/json",
  },
  body: JSON.stringify({
    ids: [
      "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
      "b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e",
    ],
  }),
});

const { payload } = await response.json();
console.log(payload.deleted_count);
payload := map[string][]string{"ids": {
    "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
    "b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e",
}}
buf, _ := json.Marshal(payload)

req, _ := http.NewRequest("POST", "https://api.proxyhat.com/v1/sub-users/bulk-delete", bytes.NewReader(buf))
req.Header.Set("Authorization", "Bearer __API_KEY__")
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json")

resp, err := http.DefaultClient.Do(req)
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()

var body struct {
    Payload map[string]interface{} `json:"payload"`
}
json.NewDecoder(resp.Body).Decode(&body)
fmt.Println(body.Payload["deleted_count"])

Bulk Move to Group

POST /v1/sub-users/bulk-move-to-group Requires Auth

Bulk Move to Group

Bulk Move to Group

Assign several sub-users to a group in one call, or pass sub_user_group_id: null to unassign them all. The target group must be one you own (see the Sub-User Groups API). Only active sub-users are moved; the payload returns the updated sub-user objects.

Example request: ids sub_user_group_id
Request Body
Name Type Required Description
ids array of string (uuid) Required Sub-user UUIDs to move. At least one required.
sub_user_group_id string (uuid) or null Optional Target group UUID (must be one you own), or null to unassign the sub-users from any group.
curl -X POST https://api.proxyhat.com/v1/sub-users/bulk-move-to-group \
  -H "Authorization: Bearer __API_KEY__" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "ids": ["a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"],
    "sub_user_group_id": "c3d4e5f6-a7b8-4c9d-0e1f-2a3b4c5d6e7f"
  }'
import requests

response = requests.post(
    "https://api.proxyhat.com/v1/sub-users/bulk-move-to-group",
    headers={
        "Authorization": "Bearer __API_KEY__",
        "Accept": "application/json",
    },
    json={
        "ids": ["a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"],
        "sub_user_group_id": "c3d4e5f6-a7b8-4c9d-0e1f-2a3b4c5d6e7f",
        # pass None to unassign instead
    },
)

for su in response.json()["payload"]:
    print(su["uuid"], su["sub_user_group_id"])
const response = await fetch("https://api.proxyhat.com/v1/sub-users/bulk-move-to-group", {
  method: "POST",
  headers: {
    "Authorization": "Bearer __API_KEY__",
    "Content-Type": "application/json",
    "Accept": "application/json",
  },
  body: JSON.stringify({
    ids: ["a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"],
    sub_user_group_id: "c3d4e5f6-a7b8-4c9d-0e1f-2a3b4c5d6e7f", // or null to unassign
  }),
});

const { payload } = await response.json();
payload.forEach(su => console.log(su.uuid, su.sub_user_group_id));
payload := map[string]interface{}{
    "ids":               []string{"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"},
    "sub_user_group_id": "c3d4e5f6-a7b8-4c9d-0e1f-2a3b4c5d6e7f", // or nil to unassign
}
buf, _ := json.Marshal(payload)

req, _ := http.NewRequest("POST", "https://api.proxyhat.com/v1/sub-users/bulk-move-to-group", bytes.NewReader(buf))
req.Header.Set("Authorization", "Bearer __API_KEY__")
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json")

resp, err := http.DefaultClient.Do(req)
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()

var body struct {
    Payload []map[string]interface{} `json:"payload"`
}
json.NewDecoder(resp.Body).Decode(&body)
for _, su := range body.Payload {
    fmt.Println(su["uuid"], su["sub_user_group_id"])
}