Proxies
A proxy is one 4G dongle on one of your nodes, published on one public port of our cloud router.
Its id is the number in the panel URL (/proxies/7).
The proxy object
{
"id": 7,
"label": "Shop A",
"host": "152.67.6.160",
"port": 30007,
"pending_host": null,
"pending_port": null,
"switch_at": null,
"status": "online",
"enabled": true,
"disabled_reason": null,
"data_connection": "connected",
"data_connection_reason": null,
"current_ip": "100.64.1.2",
"signal": {"network_type": "LTE", "rsrp": "-95dBm"},
"fingerprint": "android",
"auto_rotate_seconds": null,
"sticky": false,
"last_rotated_at": "2026-10-09T18:00:00+00:00",
"rotation_url": "https://proxykite.com/r/<rotation_token>",
"expires_at": null,
"quota_bytes": null,
"used_bytes": 123456,
"node": {"id": 12, "hostname": "proxyserver2", "online": true, "name": "Rack A", "display_name": "Rack A",
"country_code": "IN", "city": "Kolkata"},
"users": [
{"username": "u7abc", "password": "s3cret", "max_conn": 200, "enabled": true,
"http": "http://u7abc:s3cret@152.67.6.160:30007",
"socks5": "socks5://u7abc:s3cret@152.67.6.160:30007"}
],
"whitelist": [{"id": 3, "cidr": "203.0.113.4/32", "note": "office"}],
"ports": {"mode": "shared", "http_port": 30007, "socks5_port": 30007, "host": "152.67.6.160",
"range": {"min": 30000, "max": 30999}},
"site_rules": {"mode": "off", "count": 0},
"limits": {"...": "..."},
"traffic": {"...": "..."},
"device": {"...": "..."}
}
disabled_reason values: pending_approval (the node has auto-adopt off — approve the dongle under Nodes),
admin_disabled, over_limit (more dongles than the licence allows), license_suspended, subscription_expired (the licence was not
renewed: paid-until date + grace days have passed — it comes back by itself after renewal), owner_suspended, unpaid,
assignee_suspended, expired, over_quota, bandwidth_limit (a day/week/month bandwidth limit is used up — the proxy
starts again when that period resets), no_address (the dongle has no IP on the box yet).
List proxies
GET /proxies
Ability: read · Every proxy your login can see, ordered by id (up to 2000). Reseller sub-users see only the
proxies assigned to them.
curl 'https://proxykite.com/api/v1/proxies' \
-H "Authorization: Bearer $HYP_TOKEN" \
-H 'Accept: application/json'
import os
import requests
r = requests.get(
"https://proxykite.com/api/v1/proxies",
headers={"Authorization": f"Bearer {os.environ['HYP_TOKEN']}", "Accept": "application/json"},
timeout=30,
)
print(r.status_code, r.json())
const res = await fetch('https://proxykite.com/api/v1/proxies', {
headers: {
Authorization: `Bearer ${process.env.HYP_TOKEN}`,
Accept: 'application/json',
},
signal: AbortSignal.timeout(30000),
});
console.log(res.status, await res.json());
<?php
$ch = curl_init('https://proxykite.com/api/v1/proxies');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('HYP_TOKEN'),
'Accept: application/json',
],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
var_dump($status, json_decode($body, true));
Response 200: {"data": [<proxy>, ...]}
Get one proxy
GET /proxies/{id}
Ability: read
curl 'https://proxykite.com/api/v1/proxies/7' \
-H "Authorization: Bearer $HYP_TOKEN" \
-H 'Accept: application/json'
import os
import requests
r = requests.get(
"https://proxykite.com/api/v1/proxies/7",
headers={"Authorization": f"Bearer {os.environ['HYP_TOKEN']}", "Accept": "application/json"},
timeout=30,
)
print(r.status_code, r.json())
const res = await fetch('https://proxykite.com/api/v1/proxies/7', {
headers: {
Authorization: `Bearer ${process.env.HYP_TOKEN}`,
Accept: 'application/json',
},
signal: AbortSignal.timeout(30000),
});
console.log(res.status, await res.json());
<?php
$ch = curl_init('https://proxykite.com/api/v1/proxies/7');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('HYP_TOKEN'),
'Accept: application/json',
],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
var_dump($status, json_decode($body, true));
Response 200: {"data": <proxy>} · 404 not_found if the proxy does not exist or is not yours.
Rename a proxy
PATCH /proxies/{id}
Ability: manage · Who: account owner or team admin (sub-users and viewers get 403 forbidden).
curl -X PATCH 'https://proxykite.com/api/v1/proxies/7' \
-H "Authorization: Bearer $HYP_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{"label":"Shop A"}'
import os
import requests
r = requests.patch(
"https://proxykite.com/api/v1/proxies/7",
headers={"Authorization": f"Bearer {os.environ['HYP_TOKEN']}", "Accept": "application/json"},
json={"label": "Shop A"},
timeout=30,
)
print(r.status_code, r.json())
const res = await fetch('https://proxykite.com/api/v1/proxies/7', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.HYP_TOKEN}`,
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({"label":"Shop A"}),
signal: AbortSignal.timeout(30000),
});
console.log(res.status, await res.json());
<?php
$ch = curl_init('https://proxykite.com/api/v1/proxies/7');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PATCH',
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('HYP_TOKEN'),
'Accept: application/json',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => '{"label":"Shop A"}',
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
var_dump($status, json_decode($body, true));
Response 200: {"data": <proxy>} (the updated proxy).
SIM card and recharge date
PATCH /proxies/{id}
Ability: manage · Who: account owner or team admin. The SIM details of a dongle: phone number, operator,
plan note and the next recharge date. We remind you (see Alerts) about 09:00 in your timezone
remind_days_before days before, on the day, and daily for a week while overdue.
GET /proxies/{id} returns the same sim object (not for reseller sub-users). These details belong to the SIM card:
when the device reports its SIM (ICCID), a SIM moved to another dongle takes its number, recharge date and SMS limits
along. Read-only extras: id, identified (the SIM's ICCID is known), iccid_last4, sms_limits {day, week, month}.
Send only the keys you change;
null or "" clears one. sim may be sent alone or together with label / other fields.
curl -X PATCH 'https://proxykite.com/api/v1/proxies/7' \
-H "Authorization: Bearer $HYP_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{"sim":{"phone":"+919800000000","operator":"Jio","next_recharge_date":"2026-11-01","remind_days_before":3}}'
import os
import requests
r = requests.patch(
"https://proxykite.com/api/v1/proxies/7",
headers={"Authorization": f"Bearer {os.environ['HYP_TOKEN']}", "Accept": "application/json"},
json={"sim": {"phone": "+919800000000", "operator": "Jio", "next_recharge_date": "2026-11-01", "remind_days_before": 3}},
timeout=30,
)
print(r.status_code, r.json())
const res = await fetch('https://proxykite.com/api/v1/proxies/7', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.HYP_TOKEN}`,
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({"sim":{"phone":"+919800000000","operator":"Jio","next_recharge_date":"2026-11-01","remind_days_before":3}}),
signal: AbortSignal.timeout(30000),
});
console.log(res.status, await res.json());
<?php
$ch = curl_init('https://proxykite.com/api/v1/proxies/7');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PATCH',
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('HYP_TOKEN'),
'Accept: application/json',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => '{"sim":{"phone":"+919800000000","operator":"Jio","next_recharge_date":"2026-11-01","remind_days_before":3}}',
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
var_dump($status, json_decode($body, true));
Response 200: {"data": <proxy with "sim">}:
"sim": {"phone": "+919800000000", "operator": "Jio", "operator_detected": "Reliance Jio Infocomm",
"plan": null, "next_recharge_date": "2026-11-01", "remind_days_before": 3, "days_left": 22}
days_left is counted in the owner's timezone; negative = overdue. Errors name the fields sim_phone,
sim_recharge_date, … in fields.
SMS and bandwidth limits
GET /proxies/{id}/limits · PATCH /proxies/{id}
Ability: read to see them (also the limits object in every proxy), manage to change them ·
Who changes: account owner or team admin (reseller sub-users and viewers see them read-only).
Each proxy can have an SMS send limit and a bandwidth limit per day, week (Monday–Sunday) and month —
each optional. Periods are calendar periods in the account's timezone (timezone in the answer).
- SMS: every SMS the dongle accepts (panel or
POST /proxies/{id}/sms) counts. When a
limit is used up, sending is refused with 429 sms_limit_reached until that period resets.
- Bandwidth: traffic in + out of all proxy logins, checked about once a minute. At 80 % you get a
bandwidth_warning alert, at 100 % the proxy stops (enabled:false, disabled_reason:"bandwidth_limit") and you
get bandwidth_limit_reached; it starts again by itself when the period resets. Raising/removing the limit or
reset_bandwidth starts it at once.
PATCH body — send only what you change; null or 0 removes a limit:
curl -X PATCH 'https://proxykite.com/api/v1/proxies/7' \
-H "Authorization: Bearer $HYP_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{"limits":{"sms":{"day":50,"month":1000},"bandwidth_mb":{"day":5120,"month":null}}}'
import os
import requests
r = requests.patch(
"https://proxykite.com/api/v1/proxies/7",
headers={"Authorization": f"Bearer {os.environ['HYP_TOKEN']}", "Accept": "application/json"},
json={"limits": {"sms": {"day": 50, "month": 1000}, "bandwidth_mb": {"day": 5120, "month": None}}},
timeout=30,
)
print(r.status_code, r.json())
const res = await fetch('https://proxykite.com/api/v1/proxies/7', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.HYP_TOKEN}`,
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({"limits":{"sms":{"day":50,"month":1000},"bandwidth_mb":{"day":5120,"month":null}}}),
signal: AbortSignal.timeout(30000),
});
console.log(res.status, await res.json());
<?php
$ch = curl_init('https://proxykite.com/api/v1/proxies/7');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PATCH',
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('HYP_TOKEN'),
'Accept: application/json',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => '{"limits":{"sms":{"day":50,"month":1000},"bandwidth_mb":{"day":5120,"month":null}}}',
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
var_dump($status, json_decode($body, true));
Response 200: {"data": <proxy>}. The limits object (also from GET /proxies/{id}/limits as {"data": …}):
{"timezone": "Asia/Kolkata",
"sms": {"day": {"limit": 50, "used": 12, "resets_at": "2026-10-10T18:30:00+00:00"},
"week": {"limit": null, "used": 40, "resets_at": "2026-10-11T18:30:00+00:00"},
"month": {"limit": 1000, "used": 210, "resets_at": "2026-10-31T18:30:00+00:00"}},
"bandwidth": {"day": {"limit_bytes": 5368709120, "used_bytes": 4400000000, "percent": 82, "resets_at": "2026-10-10T18:30:00+00:00"},
"week": {"limit_bytes": null, "used_bytes": 9100000000, "percent": null, "resets_at": "2026-10-11T18:30:00+00:00"},
"month": {"limit_bytes": null, "used_bytes": 30500000000, "percent": null, "resets_at": "2026-10-31T18:30:00+00:00"}},
"blocked": false, "blocked_reason": null, "blocked_period": null, "blocked_until": null}
blocked: true (with blocked_period and blocked_until) while a bandwidth limit has stopped the proxy.
Usage is counted in hourly steps: with a half-hour timezone (e.g. India) the hour that straddles midnight counts to the
previous period.
curl 'https://proxykite.com/api/v1/proxies/7/limits' \
-H "Authorization: Bearer $HYP_TOKEN" \
-H 'Accept: application/json'
import os
import requests
r = requests.get(
"https://proxykite.com/api/v1/proxies/7/limits",
headers={"Authorization": f"Bearer {os.environ['HYP_TOKEN']}", "Accept": "application/json"},
timeout=30,
)
print(r.status_code, r.json())
const res = await fetch('https://proxykite.com/api/v1/proxies/7/limits', {
headers: {
Authorization: `Bearer ${process.env.HYP_TOKEN}`,
Accept: 'application/json',
},
signal: AbortSignal.timeout(30000),
});
console.log(res.status, await res.json());
<?php
$ch = curl_init('https://proxykite.com/api/v1/proxies/7/limits');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('HYP_TOKEN'),
'Accept: application/json',
],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
var_dump($status, json_decode($body, true));
Device type and capabilities
Every proxy object carries a device object. Your own logins (not reseller sub-users) also get device.identity.
"device": {
"uid": "01k7c2y9s8m3v5q1x0h4f6t2wb",
"type": "hilink",
"type_label": "Huawei HiLink dongle",
"reported": true,
"model": "E3372h-320",
"firmware": "22.333.01.00.00",
"driver_version": "1.0.0",
"state": "ok",
"control_via": "panel",
"identity_state": "confirmed",
"capabilities": {
"egress": {"s": "yes", "kind": "netdev"},
"rotate.netmode": {"s": "yes"},
"sms.send": {"s": "yes"},
"ussd": {"s": "setup", "hint": "Install ModemManager: sudo hostyourproxy setup modem"},
"web_ui": {"s": "no"}
},
"rotate_methods": ["netmode", "reconnect", "reboot"],
"identity": {"imei_last4": "0123", "moved_from_node_id": null, "moved_at": null,
"conflict_with_proxy_id": null, "restorable_from_proxy_id": null}
}
Capability names: egress, rotate.netmode, rotate.reconnect, rotate.airplane, rotate.reboot,
rotate.link_bounce, status.signal, sms.read, sms.send, sms.delete, ussd, device.reboot, web_ui,
lan.settings, netmode.set, identity.imei, identity.iccid, identity.imsi, identity.device_key,
data_connect (the box brings the device's data connection up itself, with your APN).
Show only what is yes. Asking for something the device cannot do answers 409 unsupported with the
capability that is missing (rotation, rotation links, sending SMS; the SMS list and interface endpoints only when the
device has no such thing at all; the interface endpoints when egress.kind is app).
Delete and restore
Deleting a proxy frees its public port at once and stops it. Its logins, whitelist, label and rotation link are kept for
7 days: restore them here or in the panel (Nodes → Interfaces → Recently deleted). When the same device is plugged
in again within those days, it comes back as a new proxy and the panel offers to restore the old settings.
Only the account owner or a team admin can delete or restore; reseller sub-users cannot (nor list deleted proxies).
A live proxy whose interface shows warnings.possible_duplicate_of is a likely
candidate for deleting — nothing is deleted automatically.
curl -X DELETE 'https://proxykite.com/api/v1/proxies/7' -H "Authorization: Bearer $HYP_TOKEN" -H 'Accept: application/json'
curl -X POST 'https://proxykite.com/api/v1/proxies/7/restore' -H "Authorization: Bearer $HYP_TOKEN" -H 'Accept: application/json'