ISP Proxies API

ISP proxies are discrete, static IP:port credentials assigned to your account — a fundamentally different model from the residential gateway. Where residential proxies share a single rotating gateway host (gate.proxyhat.com) and meter a byte pool across many sub-users, an ISP proxy is a dedicated fixed endpoint with its own IP, login and password that you connect to directly over HTTP or SOCKS5. These endpoints return the standard {success, payload, meta, errors, description} envelope.

Every endpoint on this page requires ISP proxy access, with one exception. They are only available to accounts with the has_isp_proxy_access flag enabled (check the has_isp_proxy_access field on GET /v1/auth/user). Without it every call returns 403 in the standard envelope: { "success": false, "payload": null, "errors": { "access": "ISP proxy access is not enabled for your account." }, "description": "Access denied" }. The single exception is Cancel Auto-Renew, which is deliberately reachable regardless of the flag — it only ever stops future spend, so it stays available even where the rest of this surface is closed.

List My ISP Proxies

GET /v1/isp-proxies Requires Auth

List My ISP Proxies

List My ISP Proxies

Return every ISP proxy assigned to the authenticated account, ordered by country then IP. The payload is a mixed array of two row kinds: live "proxy" rows (ready-to-use credentials) and "provisioning" rows (orders still being fulfilled). Distinguish them by the "kind" field. No cost or balance data is ever included.

No input needed — runs live with your key
curl https://api.proxyhat.com/v1/isp-proxies \
  -H "Authorization: Bearer __API_KEY__" \
  -H "Accept: application/json"
import requests

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

data = response.json()
for row in data["payload"]:
    if row["kind"] == "proxy":
        print(f"{row[\"isp_name\"]} {row[\"ip\"]}:{row[\"port\"]} -> {row[\"http_url\"]}")
    else:
        print(f"provisioning {row[\"isp_name\"]} (~{row[\"eta_hours\"]}h)")
const response = await fetch("https://api.proxyhat.com/v1/isp-proxies", {
  headers: {
    "Authorization": "Bearer __API_KEY__",
    "Accept": "application/json",
  },
});

const { payload } = await response.json();
payload.forEach(row => {
  if (row.kind === "proxy") {
    console.log(`${row.isp_name} ${row.ip}:${row.port} -> ${row.http_url}`);
  } else {
    console.log(`provisioning ${row.isp_name} (~${row.eta_hours}h)`);
  }
});
req, _ := http.NewRequest("GET", "https://api.proxyhat.com/v1/isp-proxies", 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 out struct {
    Payload []map[string]interface{} `json:"payload"`
}
json.NewDecoder(resp.Body).Decode(&out)
for _, row := range out.Payload {
    fmt.Printf("%v %v\n", row["kind"], row["isp_name"])
}
Two row kinds. A "proxy" row carries the full live credentials (ip, port, login, password) plus both ready-to-paste connection URLs (http_url, socks5_url). A "provisioning" row represents an order still being fulfilled (pending / provisioning / needs-attention) and carries only isp_name, country_code, category, an eta_hours estimate and queued_at — poll this endpoint until it flips to a "proxy" row. subscription_status is null when the proxy is not tied to a subscription.

Get a Single ISP Proxy

GET /v1/isp-proxies/{id} Requires Auth

Get a Single ISP Proxy

Get a Single ISP Proxy

The same row shape List My ISP Proxies returns, for exactly one proxy — useful when you already track the id and do not want to pull the whole listing. 404 for a proxy that belongs to another account or a malformed (non-UUID) id.

We filled these in for you: id

Tweak any value if you like — or just press Try.

Path Parameters
Name Type Required Description
id string (uuid) Required The proxy id from a listing.
curl https://api.proxyhat.com/v1/isp-proxies/7c5b0e3d-9a84-4e9d-a12f-8d2e1f4a6b3c \
  -H "Authorization: Bearer __API_KEY__" \
  -H "Accept: application/json"
import requests

proxy_id = "7c5b0e3d-9a84-4e9d-a12f-8d2e1f4a6b3c"
response = requests.get(
    f"https://api.proxyhat.com/v1/isp-proxies/{proxy_id}",
    headers={"Authorization": "Bearer __API_KEY__", "Accept": "application/json"},
)

print(response.json()["payload"])
const proxyId = "7c5b0e3d-9a84-4e9d-a12f-8d2e1f4a6b3c";

const response = await fetch(`https://api.proxyhat.com/v1/isp-proxies/${proxyId}`, {
  headers: { "Authorization": "Bearer __API_KEY__", "Accept": "application/json" },
});

console.log((await response.json()).payload);
proxyID := "7c5b0e3d-9a84-4e9d-a12f-8d2e1f4a6b3c"
req, _ := http.NewRequest("GET", "https://api.proxyhat.com/v1/isp-proxies/"+proxyID, 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 out struct {
    Payload map[string]interface{} `json:"payload"`
}
json.NewDecoder(resp.Body).Decode(&out)
fmt.Println(out.Payload)

Update Your Note on a Proxy

PATCH /v1/isp-proxies/{id} Requires Auth

Update Your Note on a Proxy

Update Your Note on a Proxy

Write a free-text label on one of your own proxies (the same field the dashboard's inline-edit uses). Send "" or null to clear it — both are equivalent, and it always reads back as null, never "". Rate-limited to 60 requests/minute.

Example request: id
Path Parameters
Name Type Required Description
id string (uuid) Required The proxy id.
Request Body
Name Type Required Description
notes string Required Max 255 characters. Must be present in the request (may be "" or null to clear).
curl -X PATCH https://api.proxyhat.com/v1/isp-proxies/7c5b0e3d-9a84-4e9d-a12f-8d2e1f4a6b3c \
  -H "Authorization: Bearer __API_KEY__" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"notes": "client: acme corp"}'
import requests

proxy_id = "7c5b0e3d-9a84-4e9d-a12f-8d2e1f4a6b3c"
response = requests.patch(
    f"https://api.proxyhat.com/v1/isp-proxies/{proxy_id}",
    headers={
        "Authorization": "Bearer __API_KEY__",
        "Content-Type": "application/json",
        "Accept": "application/json",
    },
    json={"notes": "client: acme corp"},
)

print(response.json()["payload"])
const proxyId = "7c5b0e3d-9a84-4e9d-a12f-8d2e1f4a6b3c";

const response = await fetch(`https://api.proxyhat.com/v1/isp-proxies/${proxyId}`, {
  method: "PATCH",
  headers: {
    "Authorization": "Bearer __API_KEY__",
    "Content-Type": "application/json",
    "Accept": "application/json",
  },
  body: JSON.stringify({ notes: "client: acme corp" }),
});

console.log((await response.json()).payload);
proxyID := "7c5b0e3d-9a84-4e9d-a12f-8d2e1f4a6b3c"

body, _ := json.Marshal(map[string]string{"notes": "client: acme corp"})

req, _ := http.NewRequest("PATCH", "https://api.proxyhat.com/v1/isp-proxies/"+proxyID, bytes.NewReader(body))
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 out struct {
    Payload map[string]interface{} `json:"payload"`
}
json.NewDecoder(resp.Body).Decode(&out)
fmt.Println(out.Payload)

Test Proxy Connectivity

POST /v1/isp-proxies/{id}/test Requires Auth

Test Proxy Connectivity

Test Proxy Connectivity

Live server-side connectivity probe of one proxy on every protocol it serves. Shares the same prober as the dashboard's own test button, so the two surfaces never disagree about the same IP. Rate-limited to 30 requests/minute.

Example request: id
Path Parameters
Name Type Required Description
id string (uuid) Required The proxy id.
curl -X POST https://api.proxyhat.com/v1/isp-proxies/7c5b0e3d-9a84-4e9d-a12f-8d2e1f4a6b3c/test \
  -H "Authorization: Bearer __API_KEY__" \
  -H "Accept: application/json"
import requests

proxy_id = "7c5b0e3d-9a84-4e9d-a12f-8d2e1f4a6b3c"
response = requests.post(
    f"https://api.proxyhat.com/v1/isp-proxies/{proxy_id}/test",
    headers={"Authorization": "Bearer __API_KEY__", "Accept": "application/json"},
)

result = response.json()
print(result["payload"]["protocols_ok"], "/", result["payload"]["protocols_total"])
const proxyId = "7c5b0e3d-9a84-4e9d-a12f-8d2e1f4a6b3c";

const response = await fetch(`https://api.proxyhat.com/v1/isp-proxies/${proxyId}/test`, {
  method: "POST",
  headers: { "Authorization": "Bearer __API_KEY__", "Accept": "application/json" },
});

const { payload } = await response.json();
console.log(payload.protocols_ok, "/", payload.protocols_total);
proxyID := "7c5b0e3d-9a84-4e9d-a12f-8d2e1f4a6b3c"
req, _ := http.NewRequest("POST", "https://api.proxyhat.com/v1/isp-proxies/"+proxyID+"/test", 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 out struct {
    Payload map[string]interface{} `json:"payload"`
}
json.NewDecoder(resp.Body).Decode(&out)
fmt.Println(out.Payload["protocols_ok"], "/", out.Payload["protocols_total"])
An expired proxy still answers with HTTP 200, not an error status. { "success": false, "payload": null, "meta": null, "error_code": "proxy_expired", "description": "This proxy has expired. Renew it or buy a new one to keep using it." } — no network request is made to a dead IP, but the HTTP status is 200. Branch on the JSON body's success field, never on the status code, when reading this endpoint's result. This response also omits the errors key entirely (it uses error_code instead), unlike every other envelope response on this page. This is a different shape and status from the Reseller API's equivalent endpoint, which returns a real 422 for the same condition.

ISP Proxy History

GET /v1/isp-proxies/history Requires Auth

ISP Proxy History

ISP Proxy History

Your expired proxies plus your renewal ledger — the read-only "what happened to my ISP proxies" view. There is no purchase or renewal endpoint here; the only money path for ISP proxies is the Reseller API.

No input needed — runs live with your key
curl https://api.proxyhat.com/v1/isp-proxies/history \
  -H "Authorization: Bearer __API_KEY__" \
  -H "Accept: application/json"
import requests

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

data = response.json()["payload"]
print(len(data["proxies"]), "expired proxies,", len(data["renewals"]), "renewal records")
const response = await fetch("https://api.proxyhat.com/v1/isp-proxies/history", {
  headers: { "Authorization": "Bearer __API_KEY__", "Accept": "application/json" },
});

const { payload } = await response.json();
console.log(payload.proxies.length, "expired proxies,", payload.renewals.length, "renewal records");
req, _ := http.NewRequest("GET", "https://api.proxyhat.com/v1/isp-proxies/history", 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 out struct {
    Payload struct {
        Proxies  []map[string]interface{} `json:"proxies"`
        Renewals []map[string]interface{} `json:"renewals"`
    } `json:"payload"`
}
json.NewDecoder(resp.Body).Decode(&out)
fmt.Println(len(out.Payload.Proxies), "expired proxies,", len(out.Payload.Renewals), "renewal records")
Capped at 50 rows each, with no pagination cursor. Both payload.proxies and payload.renewals are hard-limited to the 50 most recent rows (by expires_at and created_at respectively). There is no page/per_page parameter and no cursor to fetch older rows — a 51st expired proxy or renewal is simply not returned. Do not rely on this endpoint as a complete historical export.

Cancel Auto-Renew

POST /v1/isp-subscriptions/{id}/cancel Requires Auth

Cancel Auto-Renew

Cancel Auto-Renew

Stop future auto-renewal on one of your ISP subscriptions — it only ever stops future spend, never the proxy itself, so it stays available even where purchase/renewal is not. Cancels any underlying card subscription too, and flips auto_renew off on every proxy tied to this subscription.

Example request:
Path Parameters
Name Type Required Description
id string (uuid) Required The isp_subscription_id from a proxy row (List My ISP Proxies), not the proxy id itself.
curl -X POST https://api.proxyhat.com/v1/isp-subscriptions/3f7a91d5-8b02-4c6e-9a1f-5d4e2c8b0a63/cancel \
  -H "Authorization: Bearer __API_KEY__" \
  -H "Accept: application/json"
import requests

sub_id = "3f7a91d5-8b02-4c6e-9a1f-5d4e2c8b0a63"
response = requests.post(
    f"https://api.proxyhat.com/v1/isp-subscriptions/{sub_id}/cancel",
    headers={"Authorization": "Bearer __API_KEY__", "Accept": "application/json"},
)

print(response.json())
const subId = "3f7a91d5-8b02-4c6e-9a1f-5d4e2c8b0a63";

const response = await fetch(`https://api.proxyhat.com/v1/isp-subscriptions/${subId}/cancel`, {
  method: "POST",
  headers: { "Authorization": "Bearer __API_KEY__", "Accept": "application/json" },
});

console.log(await response.json());
subID := "3f7a91d5-8b02-4c6e-9a1f-5d4e2c8b0a63"
req, _ := http.NewRequest("POST", "https://api.proxyhat.com/v1/isp-subscriptions/"+subID+"/cancel", 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 out map[string]interface{}
json.NewDecoder(resp.Body).Decode(&out)
fmt.Println(out)

ISP Store Catalog

GET /v1/isp-store/catalog Requires Auth

Browse ISP Store Catalog

Browse ISP Store Catalog

Return the purchasable ISP-proxy catalog — every active plan, ordered by country then ISP name. Each item is annotated with an "affordable" boolean computed against your account balance (cost figures themselves are never exposed). The payload is cached for 90 seconds.

No input needed — runs live with your key
curl https://api.proxyhat.com/v1/isp-store/catalog \
  -H "Authorization: Bearer __API_KEY__" \
  -H "Accept: application/json"
import requests

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

data = response.json()
print("max per order:", data["meta"]["max_buy_qty"])
for item in data["payload"]:
    tag = "buy" if item["affordable"] else "request"
    print(f"{item[\"isp_name\"]} ({item[\"country_code\"]}) ${item[\"price\"]} [{tag}]")
const response = await fetch("https://api.proxyhat.com/v1/isp-store/catalog", {
  headers: {
    "Authorization": "Bearer __API_KEY__",
    "Accept": "application/json",
  },
});

const { payload, meta } = await response.json();
console.log("max per order:", meta.max_buy_qty);
payload.forEach(item => {
  const tag = item.affordable ? "buy" : "request";
  console.log(`${item.isp_name} (${item.country_code}) $${item.price} [${tag}]`);
});
req, _ := http.NewRequest("GET", "https://api.proxyhat.com/v1/isp-store/catalog", 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 out struct {
    Payload []map[string]interface{} `json:"payload"`
    Meta    struct {
        MaxBuyQty   int  `json:"max_buy_qty"`
        RequestGate bool `json:"request_gate"`
    } `json:"meta"`
}
json.NewDecoder(resp.Body).Decode(&out)
fmt.Println("max per order:", out.Meta.MaxBuyQty)
for _, item := range out.Payload {
    fmt.Printf("%v (%v) $%v affordable=%v\n", item["isp_name"], item["country_code"], item["price"], item["affordable"])
}
Catalog fields. price is the sell price in currency; days is the subscription term; traffic_limit of 0 means the ISP proxy is unmetered. affordable tells you whether the plan can be bought outright right now — when it is false, open a top-up request with the tariff-request endpoint below instead. meta.max_buy_qty caps how many units may be bought in a single order, and meta.request_gate indicates whether direct purchase is gated behind the request flow.

Request a Tariff (Top-Up)

POST /v1/isp-store/tariff-request Requires Auth

Request a Tariff

Request a Tariff

Open a top-up request for an ISP plan you cannot yet afford. This posts a tagged message into your support conversation and alerts an admin to add balance. Deduplicated within a 24-hour window (a repeat call inside the window returns 200 without re-posting or re-notifying). Rate-limited to 10 requests per minute.

Example request: plan_id
Request Body
Name Type Required Description
plan_id string (uuid) Required The UUID of the ISP plan (the "id" from a catalog item) you want to request.
curl -X POST https://api.proxyhat.com/v1/isp-store/tariff-request \
  -H "Authorization: Bearer __API_KEY__" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"plan_id": "8d2e1f4a-6b3c-4e9d-a12f-7c5b0e3d9a84"}'
import requests

response = requests.post(
    "https://api.proxyhat.com/v1/isp-store/tariff-request",
    headers={
        "Authorization": "Bearer __API_KEY__",
        "Content-Type": "application/json",
        "Accept": "application/json",
    },
    json={"plan_id": "8d2e1f4a-6b3c-4e9d-a12f-7c5b0e3d9a84"},
)

print(response.json())
const response = await fetch("https://api.proxyhat.com/v1/isp-store/tariff-request", {
  method: "POST",
  headers: {
    "Authorization": "Bearer __API_KEY__",
    "Content-Type": "application/json",
    "Accept": "application/json",
  },
  body: JSON.stringify({ plan_id: "8d2e1f4a-6b3c-4e9d-a12f-7c5b0e3d9a84" }),
});

console.log(await response.json());
body, _ := json.Marshal(map[string]string{
    "plan_id": "8d2e1f4a-6b3c-4e9d-a12f-7c5b0e3d9a84",
})

req, _ := http.NewRequest("POST", "https://api.proxyhat.com/v1/isp-store/tariff-request", bytes.NewReader(body))
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 out map[string]interface{}
json.NewDecoder(resp.Body).Decode(&out)
fmt.Println(out)
This endpoint is only for plans you cannot afford. If the plan is already affordable the server returns 422 with { "success": false, "errors": { "plan": "This tariff is available — buy it directly." }, "description": "Plan is affordable" } — buy it directly instead. An unknown or inactive plan_id returns 404 { "message": "Not found" }, and a malformed (non-UUID) plan_id returns 422 validation error.

Get ISP Plan

GET /v1/plans/isp/{id} Requires Auth

Get ISP Plan

Get ISP Plan

Retrieve a single active ISP plan by its UUID, for a checkout / order-summary screen. Internal cost and markup fields are stripped from the response, and a "name" alias (equal to "isp_name") is added for convenience. Returns a flat plan object (NOT wrapped in the envelope); 404 if the plan does not exist or is inactive.

We filled these in for you: id

Tweak any value if you like — or just press Try.

Path Parameters
Name Type Required Description
id string (uuid) Required The UUID of the ISP plan (the "id" from a catalog item).
curl https://api.proxyhat.com/v1/plans/isp/8d2e1f4a-6b3c-4e9d-a12f-7c5b0e3d9a84 \
  -H "Authorization: Bearer __API_KEY__" \
  -H "Accept: application/json"
import requests

plan_id = "8d2e1f4a-6b3c-4e9d-a12f-7c5b0e3d9a84"

response = requests.get(
    f"https://api.proxyhat.com/v1/plans/isp/{plan_id}",
    headers={
        "Authorization": "Bearer __API_KEY__",
        "Accept": "application/json",
    },
)

plan = response.json()
print(f"{plan[\"name\"]} ({plan[\"country_code\"]}): ${plan[\"price_total\"]}")
const planId = "8d2e1f4a-6b3c-4e9d-a12f-7c5b0e3d9a84";

const response = await fetch(`https://api.proxyhat.com/v1/plans/isp/${planId}`, {
  headers: {
    "Authorization": "Bearer __API_KEY__",
    "Accept": "application/json",
  },
});

const plan = await response.json();
console.log(`${plan.name} (${plan.country_code}): $${plan.price_total}`);
planID := "8d2e1f4a-6b3c-4e9d-a12f-7c5b0e3d9a84"
url := fmt.Sprintf("https://api.proxyhat.com/v1/plans/isp/%s", planID)

req, _ := http.NewRequest("GET", url, 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 plan map[string]interface{}
json.NewDecoder(resp.Body).Decode(&plan)
fmt.Printf("%s (%s): $%v\n", plan["name"], plan["country_code"], plan["price_total"])