Quickstart
Three steps: create a key, call an endpoint, read the envelope. You need an organization with a live plan.
1. Create an API key
Open the Developer page in the dashboard and create a key. The full value is shown once, at creation — we store only a hash, so we cannot show it to you again. Copy it into your secret manager before you close the dialog.
2. Make a request
Pass the key as a bearer token. GET /proxies is a good first call: it is read-only and returns the credentials your proxy client needs.
curl https://api.proxycharm.com/api/v1/public/proxies \
-H "Authorization: Bearer $PROXYCHARM_API_KEY" import os
import requests
API_KEY = os.environ["PROXYCHARM_API_KEY"]
response = requests.get(
"https://api.proxycharm.com/api/v1/public/proxies",
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=30,
)
payload = response.json()
if not payload["success"]:
raise RuntimeError(payload["error"]["message"])
print(payload["data"]) const apiKey = process.env.PROXYCHARM_API_KEY;
const response = await fetch("https://api.proxycharm.com/api/v1/public/proxies", {
method: "GET",
headers: {
Authorization: `Bearer ${apiKey}`,
},
});
const payload = await response.json();
if (!payload.success) {
throw new Error(payload.error.message);
}
console.log(payload.data); package main
import (
"encoding/json"
"fmt"
"net/http"
"os"
)
type envelope struct {
Success bool `json:"success"`
Data json.RawMessage `json:"data"`
Error *struct {
Code string `json:"code"`
Message string `json:"message"`
} `json:"error"`
}
func main() {
req, err := http.NewRequest("GET", "https://api.proxycharm.com/api/v1/public/proxies", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("PROXYCHARM_API_KEY"))
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
var payload envelope
if err := json.NewDecoder(resp.Body).Decode(&payload); err != nil {
panic(err)
}
if !payload.Success {
panic(payload.Error.Message)
}
fmt.Println(string(payload.Data))
} <?php
$apiKey = getenv("PROXYCHARM_API_KEY");
$ch = curl_init("https://api.proxycharm.com/api/v1/public/proxies");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . $apiKey,
],
]);
$payload = json_decode(curl_exec($ch), true);
curl_close($ch);
if (!$payload["success"]) {
throw new RuntimeException($payload["error"]["message"]);
}
print_r($payload["data"]); 3. Read the response
Every response uses the same envelope. Check success first, then read data. A trimmed example:
{
"success": true,
"data": {
"items": [
{
"assignment_id": "9c1f7a2e-4b5d-4c3a-8e11-2d7b6f0a91c4",
"proxy_ip_id": "2f8e5d13-9a7c-4f62-b0d4-6e3a1c58b7f9",
"ip_address": "203.0.113.42",
"country": "US",
"city": "Chicago",
"status": "live",
"host": "proxy.proxycharm.com",
"port": 10000,
"username": "pc_9c1f7a2e",
"password": "s3cr3t-value",
"replaced_at": null
}
],
"total": 1
},
"error": null,
"request_id": null,
"timestamp": "2026-02-18T09:31:04.512Z"
} host, port, username, and password are the gateway credentials for that assignment — the same values you see on the Proxies page. Paste them into any HTTP client that supports proxies.
4. Handle failures
Two things to get right before you ship. First, a success: false body can arrive with HTTP 200, so never branch on the status code alone. Second, respect Retry-After on HTTP 429.
import os
import time
import requests
API_KEY = os.environ["PROXYCHARM_API_KEY"]
BASE_URL = "https://api.proxycharm.com/api/v1/public"
def call(method, path, **kwargs):
"""One request with rate-limit backoff and envelope unwrapping."""
while True:
response = requests.request(
method,
f"{BASE_URL}{path}",
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=30,
**kwargs,
)
if response.status_code == 429:
time.sleep(int(response.headers.get("Retry-After", "1")))
continue
if response.status_code >= 400:
# Transport failure: the code is under detail.
detail = response.json().get("detail", {})
raise RuntimeError(detail.get("code", "HTTP_ERROR"))
payload = response.json()
if not payload["success"]:
# Business failure: HTTP 200, code under error.
raise RuntimeError(payload["error"]["code"])
return payload["data"]
print(call("GET", "/proxies")) const apiKey = process.env.PROXYCHARM_API_KEY;
const baseUrl = "https://api.proxycharm.com/api/v1/public";
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
/** One request with rate-limit backoff and envelope unwrapping. */
export async function call(method, path, body) {
for (;;) {
const response = await fetch(`${baseUrl}${path}`, {
method,
headers: {
Authorization: `Bearer ${apiKey}`,
...(body ? { "Content-Type": "application/json" } : {}),
},
body: body ? JSON.stringify(body) : undefined,
});
if (response.status === 429) {
await sleep(Number(response.headers.get("Retry-After") ?? 1) * 1000);
continue;
}
const payload = await response.json();
if (!response.ok) {
// Transport failure: the code is under detail.
throw new Error(payload.detail?.code ?? "HTTP_ERROR");
}
if (!payload.success) {
// Business failure: HTTP 200, code under error.
throw new Error(payload.error.code);
}
return payload.data;
}
}
console.log(await call("GET", "/proxies")); You are set up
From here, the conventions page explains the envelope in full and the reference documents every endpoint.