ProxyHat is built to be resold. One ProxyHat account holds a single paid traffic pool, and you carve that pool into sub-users — one per downstream customer, each with its own proxy credentials and its own byte cap that ProxyHat enforces upstream. This guide covers the model, the one mistake that catches every new reseller, and three ways to run your business: by hand in the dashboard, programmatically through the /v1 API embedded in your own site or software, and from the Telegram bot on your phone.
The Model
There is no separate "reseller mode" to switch on — the sub-user system is the reseller system. Three things are worth internalising before you sell anything:
Your account = one traffic pool. When you buy traffic (pay-as-you-go plans, a subscription, or a coupon top-up) the bytes land in a single shared balance on your account. That balance is your sellable inventory.
Each sub-user = one downstream customer. A sub-user has its own auto-generated proxy_username, a proxy_password you choose, and a traffic_limit in bytes. The customer connects with those credentials to gate.proxyhat.com; you never expose your own account.
The cap is enforced upstream, not just displayed. A sub-user's traffic_limit is pushed to the upstream provider (NodeMaven). When the customer hits it, the proxy stops working — you are not trusting the customer to stay under a number.
All three surfaces — dashboard, API, and Telegram bot — drive the same core, so a sub-user you create in the dashboard is visible and editable over the API and vice versa.
How the pool splits up. Every capped sub-user reserves its unused headroom from the pool: reserved = max(traffic_limit − used_traffic, 0). What's left to hand out is traffic_available = traffic_total_remaining − Σ reserved. You can read all three numbers from the meta block on GET /v1/sub-users. You cannot allocate more than traffic_available — over-allocation is rejected with a 422, so you can never oversell beyond the pool you've actually bought.
The #1 Trap — Read This First
Topping up your pool does NOT automatically raise an existing customer's traffic_limit. Buying more traffic only grows the shared pool. A sub-user that has run out stays dead at its old cap until you explicitly raise it. There is exactly one exception: if your account has a single active proxy, a credit auto-allocates to it. Every reseller has many sub-users, so this auto-allocation never fires for you. After any top-up you must raise each customer you're renewing with PUT /v1/sub-users (or the dashboard edit form, or a bot PATCH). Paying for more traffic and forgetting this step is the single most common reseller error.
Four more rules that follow from how caps and the pool work:
traffic_limit is in bytes. 1 GB = 1000000000, 5 GB = 5000000000, 10 GB = 10000000000. Do the multiplication before you send it.
traffic_limit = 0 means UNCAPPED, not zero. A sub-user set to 0 draws on your whole available pool. Only leave it at 0 when you deliberately want an unmetered line — otherwise set a real positive cap or you're handing a customer your entire inventory.
A customer at its limit auto-suspends — and blocks new sign-ups. When a sub-user exhausts its cap it is suspended (reason: traffic overage). While any sub-user is suspended, creating new sub-users is blocked with a validation error. Raise the exhausted customer's limit (or delete it) before onboarding more.
Hard cap of 100 sub-users per account. A larger operation needs multiple ProxyHat accounts.
Resetting usage does not refund the pool.reset-usage zeroes a customer's meter for a new billing cycle, but the bytes they actually spent are already debited from your pool — it is not a way to reclaim traffic. Deletes are also soft: the customer's password is invalidated instantly (they disconnect at once), and the upstream account is torn down about 15 minutes later.
Flow A — Resell Manually via the Dashboard
The fastest way to start, and all you need for a handful of customers. Everything below is at dashboard.proxyhat.com.
Buy a traffic pool. Go to Buy traffic (or start a subscription). This grows your account balance — your sellable inventory. Buy enough to cover the sum of the caps you plan to sell.
(Optional) Create groups. Under Sub-users → Groups, add a group per plan tier or per business customer. Groups are just organisational labels scoped to your account.
Create one sub-user per customer.Sub-users → New. Set a proxy password (9–40 alphanumeric characters), a traffic limit in GB (the customer's cap — leave it unlimited only on purpose), a name you'll recognise them by, and optionally a group.
Generate the connection string. Open the proxy builder for that sub-user, pick the protocol (SOCKS5 on port 1080 is recommended; HTTP on 8080 for browser tooling), choose country / region / city, rotating vs. sticky (30m or 12h), and an AI quality filter. Copy the rendered URL — protocol://username:password@gate.proxyhat.com:port — and hand it to your customer. Everything (geo, session, filter) is baked into the username, so the URL is all they need.
Monitor. The sub-users list shows each customer's used traffic against their limit; Analytics drills into traffic, requests, and per-domain breakdown. The traffic summary shows how much pool you have left to sell.
Renew / top up. Buy more pool if you're low, then edit that specific customer and raise their traffic limit (remember the trap — the top-up alone does nothing). Optionally hit Reset usage to zero their meter for a fresh cycle.
Churn. Lower a customer's limit, or delete the sub-user — their password is invalidated immediately and they disconnect at once.
Flow B — Resell Programmatically via the API
To sell proxies from your own website, panel, or software, drive the /v1 API directly. The base URL is https://api.proxyhat.com/v1 and every call below is authenticated with a Sanctum bearer token: Authorization: Bearer __API_KEY__. The typical lifecycle is: get a key → check inventory → provision a customer → mint their URL → meter → top up / reset. The sub-user endpoints all return the standard {success, payload, meta, errors, description} envelope.
1. Get an API key
You can create a key in the dashboard under Settings → API keys, or mint one programmatically. The plaintext token is returned once — store it immediately.
POST/v1/profile/api-keysRequires Auth
Create an API Key
Create an API Key
Mint a new Sanctum bearer token for your account. The plain_text_token is shown only in this response — you cannot retrieve it again. abilities defaults to ["*"] (full scope); pass a narrower array to scope a token per integration.
Example request:
Request Body
Name
Type
Required
Description
name
string
Optional
A label for the key (defaults to "API key <timestamp>").
import requests
response = requests.post(
"https://api.proxyhat.com/v1/profile/api-keys",
headers={
"Authorization": "Bearer __API_KEY__",
"Accept": "application/json",
},
json={"name": "reseller-integration"},
)
data = response.json()
# Store this now — it is never shown again.
print(data["plain_text_token"])
const response = await fetch("https://api.proxyhat.com/v1/profile/api-keys", {
method: "POST",
headers: {
"Authorization": "Bearer __API_KEY__",
"Content-Type": "application/json",
"Accept": "application/json",
},
body: JSON.stringify({ name: "reseller-integration" }),
});
const data = await response.json();
// Store this now — it is never shown again.
console.log(data.plain_text_token);
Changing your account password revokes every API key. If your integration rotates the account password it will invalidate its own token (and all others). Keep password changes and token issuance separate, and re-issue tokens after any password change. If you enable 2FA on the account, every mutating sub-user call additionally requires a twofa_code in the body — run automation on an account without 2FA, or supply a fresh code per call.
2. Check your inventory
Before provisioning, read meta.traffic_available — the bytes free to allocate right now. The same call lists every existing customer with their usage, so it doubles as your monitoring endpoint (step 5).
GET/v1/sub-usersRequires Auth
List Sub-Users & Read Available Pool
List Sub-Users & Read Available Pool
Returns all of your sub-users (each enriched with used_traffic and a last-24h stats_used_traffic) plus a meta block with your account traffic accounting. Pass ?uuid= to fetch a single sub-user — there is no dedicated single-resource GET.
No input needed —runs live with your key
Query Parameters
Name
Type
Required
Description
uuid
string (uuid)
Optional
Filter to a single sub-user. Returns a one-element payload array (empty if it is not yours).
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"`
Meta map[string]interface{} `json:"meta"`
}
json.NewDecoder(resp.Body).Decode(&body)
fmt.Printf("%v bytes free to allocate\n", body.Meta["traffic_available"])
3. Provision a customer
Create one sub-user per customer. You supply proxy_password and (almost always) a positive traffic_limit; the proxy_username is generated server-side and is immutable afterward. Creation is confirmed synchronously, but the upstream provider registration is deferred a moment — while provisioning is true the credentials briefly return 401. Poll GET /v1/sub-users?uuid= until provisioning flips to false if you need to be certain before handing out the URL.
POST/v1/sub-usersRequires Auth
Create a Sub-User (One per Customer)
Create a Sub-User (One per Customer)
Provision a downstream customer. traffic_limit is in bytes; 0 means uncapped (draws your whole available pool), so set a real positive cap to resell a fixed quota. Rejected with 422 if the requested limit exceeds traffic_available. Blocked if any of your sub-users is currently suspended for overage.
Example request:
Request Body
Name
Type
Required
Description
proxy_password
string
Required
9–40 characters, alphanumeric only (/^[a-zA-Z0-9]*$/). The customer's proxy password.
traffic_limit
integer
Optional
The customer's cap in BYTES. 0 = uncapped. Must fit within traffic_available.
name
string
Optional
Your label for the customer (max 40 chars).
notes
string
Optional
Private notes (max 5000 chars).
sub_user_group_id
string (uuid)
Optional
Assign to one of your groups. Must be a group you own.
Rather than teaching customers the username grammar, let the server build it. POST /v1/proxy-descriptors takes the sub-user UUID plus structured location, session, and filter options and returns the fully assembled username, password, host, port, and a ready-to-paste url. Deliver the url to your customer — nothing else is required. The sub-user must be active (a suspended or deleting one returns 404).
POST/v1/proxy-descriptorsRequires Auth
Mint a Connection URL for a Customer
Mint a Connection URL for a Customer
Compose a ready-to-use proxy connection string for one of your active sub-users. All targeting (country/region/city, sticky session, AI filter) is encoded into the returned username. host is always gate.proxyhat.com; port is 8080 for http, 1080 for socks5.
We filled these in for you:
sub_user auto
Tweak any value if you like — or just press Try.
Request Body
Name
Type
Required
Description
sub_user_uuid
string (uuid)
Required
The customer sub-user. Must be yours and active.
protocol
string
Required
One of "http" or "socks5".
location
object
Optional
Geo targeting: location.country (ISO, max 32), location.region (max 128), location.city (max 128). Omit country to default to any.
session
object
Optional
session.mode = "rotating" (default) or "sticky"; session.ttl = "30m" or "12h"; session.id = 16 hex chars (auto-generated if omitted for sticky).
filter
string
Optional
AI quality filter: filter-high, filter-high-speed-fast, filter-medium (default), filter-medium-speed-fast, or none.
Want to understand the username grammar itself? See Connecting to a Proxy for the full token order (-country-, -region-, -city-, sticky -sid-/-ttl-, and the AI filter) and the list of gateway hosts and ports.
5. Meter and bill
Poll GET /v1/sub-users (step 2) for each customer's used_traffic against their traffic_limit, plus a stats_used_traffic figure for the last 24 hours. For deeper reporting the Analytics API gives you per-customer time series and per-domain breakdowns — pass the sub-user UUID as the user parameter.
6. Top up / renew a customer
This is where the trap bites. After (optionally) buying more pool, raise the specific customer's cap. The update controller reads the target from the uuid in the JSON body — the {sub_user} path segment is ignored, so any placeholder there works. Only send the fields you're changing; a traffic_limit above traffic_available is rejected with a 422. Set traffic_limit to 0 to uncap the customer entirely.
PUT/v1/sub-users/{sub_user}Requires Auth
Raise a Customer's Traffic Limit
Raise a Customer's Traffic Limit
Update a customer's cap (or password / name / group). The target sub-user is identified by the uuid in the BODY, not the path segment. PATCH is also accepted. A pool top-up does NOT do this for you — this explicit call is mandatory per customer you renew.
Example request:
sub_user auto
Path Parameters
Name
Type
Required
Description
sub_user
string
Required
Ignored — a value is only needed to form a valid URL. The body uuid selects the target.
Request Body
Name
Type
Required
Description
uuid
string (uuid)
Required
UUID of the customer to update. This — not the path — determines the target.
traffic_limit
integer
Optional
New cap in BYTES. Must fit within traffic_available; cannot drop below already-used bytes. 0 = uncapped.
proxy_password
string
Optional
9–40 alphanumeric chars, to rotate the customer's password.
name
string
Optional
Rename the customer (max 40 chars).
sub_user_group_id
string (uuid)
Optional
Move to a group, or null to unassign.
# The URL path segment is ignored; the body uuid selects the target.
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
# Raise this customer to 10 GB. The path segment is a throwaway.
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": 10 * 1000**3,
},
)
print(response.json()["payload"]["traffic_limit"])
// The URL path segment is ignored; the body uuid selects the target.
payload := strings.NewReader(`{
"uuid": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"traffic_limit": 10000000000
}`)
req, _ := http.NewRequest("PUT", "https://api.proxyhat.com/v1/sub-users/_", payload)
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"])
Lowering a limit is rate-limited. Decreases are guarded by a 120-second lock and return 429 with a Retry-After hint if you call them back-to-back; you also cannot drop a cap below the bytes the customer has already used. Increases have no such limit.
7. Start a new billing cycle
To reset a customer's meter at the start of a period, call reset-usage with their UUID(s). This zeroes used_traffic so they can consume up to their cap again — it does not refund the pool (the bytes were already spent). Registered before the resource routes, so it accepts a plain ids array.
POST/v1/sub-users/reset-usageRequires Auth
Reset a Customer's Usage Meter
Reset a Customer's Usage Meter
Zero the used_traffic counter for one or more sub-users (e.g. at the start of each billing cycle). Returns the affected sub-users plus refreshed account meta. Does not credit the pool.
Once you run many customers, organise them with groups and act on them in bulk. Full reference lives on the Sub-User Groups and Sub-Users pages; the essentials:
Create a group — POST /v1/sub-user-groups with {name, description}. Groups are labels scoped to your account (e.g. one per plan tier). Deleting a group unassigns its members rather than deleting them.
Move customers into a group — POST /v1/sub-users/bulk-move-to-group with {ids: [...], sub_user_group_id} (null to unassign).
Offboard in bulk — POST /v1/sub-users/bulk-delete with {ids: [...]}. Returns a per-UUID breakdown (deleted / skipped / not_found / failed); your default sub-user is skipped, and each deleted customer's password is invalidated immediately.
# Create a group, then move two customers into it
curl -X POST https://api.proxyhat.com/v1/sub-user-groups \
-H "Authorization: Bearer __API_KEY__" \
-H "Content-Type: application/json" \
-d '{"name": "Tier 1", "description": "5 GB monthly customers"}'
curl -X POST https://api.proxyhat.com/v1/sub-users/bulk-move-to-group \
-H "Authorization: Bearer __API_KEY__" \
-H "Content-Type: application/json" \
-d '{
"ids": ["a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d", "b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e"],
"sub_user_group_id": "c3d4e5f6-a7b8-4c9d-0e1f-2a3b4c5d6e7f"
}'
If you'd rather run everything from your phone, @ProxyHatBot exposes the same account. Link your Telegram to your ProxyHat account once (bot /start → link/verify); after that every action operates on that single linked account. The bot is single-tenant — all your sub-users live under your one account — so it's ideal for a solo reseller, not for handing separate logins to end customers.
Fund the pool. Top up traffic from inside the bot (card, crypto, or Telegram Stars). It grows the exact same sellable pool the dashboard and API use.
Create a customer sub-user. The bot's Create screen offers preset caps — 100 MB, 500 MB, 1 GB, 3 GB, 5 GB, or Max — and posts your choice in bytes. The proxy_password is generated for you server-side (you never type it into chat). The credentials are shown for active sub-users.
Hand over a ready URL with quick-proxy. Pick a preset — us_residential, eu_residential, random, or country_specific (with a country ISO code) — and a session — rotating, sticky_30m, or sticky_12h. The bot composes the full username, password, and connection URL (plus a tappable tg://socks?… link) with no modifier syntax to learn. Two taps and the customer has a working proxy.
Top up / rename a customer. Edit the customer and set a new traffic limit or name. As always, this explicit per-customer raise is what actually renews them — a pool top-up alone won't. If you lower a limit too quickly the bot may ask you to retry in a moment (the same 120-second rate-limit as the API).
Offboard. Delete the customer from the bot; their password is invalidated immediately and they disconnect at once.
Under the hood. The bot drives the same core through its own Telegram-authenticated endpoints (GET/POST /v1/bot/sub-users, PATCH/DELETE /v1/bot/sub-users/{uuid}, and POST /v1/bot/quick-proxy). These are authenticated by your linked Telegram identity, not a bearer token, so they're driven from within the bot rather than called directly from your own software. For programmatic reselling from your own site, use the /v1 API in Flow B.
Next Steps
Sub-Users API — full reference for create / update / delete / bulk operations.