Skip to content

Idempotency-Key

POST /orders and POST /orders/{id}/renew require a unique Idempotency-Key header for each new order or renewal. On a timeout it is safe to retry with the same value: the completed request is returned again (meta.idempotent_replay: true) and you are never charged twice.

Idempotency-Key: 3f7a1c9e-2b48-4d51-9c3a-8e6f0d215b47

Generate the key once

Generate a UUID for each new order or renewal and keep it in a variable.

On a timeout, retry with the same key

If no response came back, send the request again with the same key. Generating a new key for the retry opens a second order.

409 and Retry-After

A retry that arrives while the first request is still being processed gets 409 conflict and Retry-After; wait, then retry with the same key.

import { randomUUID } from "node:crypto";

const key = randomUUID(); // the SAME value on every retry

async function createOrder(body) {
  for (let attempt = 0; attempt < 3; attempt++) {
    const res = await fetch("https://api.proxynet.io/api/v1/orders", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.PROXYNET_TOKEN}`,
        "Idempotency-Key": key,
        "Content-Type": "application/json",
      },
      body: JSON.stringify(body),
    });
    if (res.status === 409 && res.headers.get("retry-after")) {
      await new Promise((r) => setTimeout(r, Number(res.headers.get("retry-after")) * 1000));
      continue;
    }
    return res.json();
  }
}
import os, time, uuid, requests

key = str(uuid.uuid4())  # the SAME value on every retry

def create_order(body):
    for _ in range(3):
        res = requests.post(
            "https://api.proxynet.io/api/v1/orders",
            headers={
                "Authorization": f"Bearer {os.environ['PROXYNET_TOKEN']}",
                "Idempotency-Key": key,
            },
            json=body,
        )
        if res.status_code == 409 and "Retry-After" in res.headers:
            time.sleep(int(res.headers["Retry-After"]))
            continue
        return res.json()

When a key is released

  • A key is released only if the attempt definitely did not charge you: a 4xx rejection, or a provider error whose charge was refunded (details.refunded: true). In that case the same key starts a new attempt.
  • A key is for a single operation. Sending the same key with a different body (targets, name and options included), or using it for another order or renewal, returns400 invalid_request. A key that opened an order is not accepted for a renewal, and a key used for a renewal is not accepted for an order.
  • A 409 conflict with details.order_id means the order opened with that key is no longer active (it failed before an automatic refund could be made, or it was cancelled). Use a new key; if you were charged, contact support.

Is the order ready

meta.ready tells you whether the order is already active, in a replayed response too. It is false for a pre-order, a scheduled activation or a provider delay, andmeta.warning is filled in. Don't put the order to use yet; poll GET /orders/{id} until state is active.

Credentials

The username and password are optional. If you don't send them, the system generates them; they are returned in the response if the token also has proxies:read. Choosing them yourself requires your plan to allow it: check the plan.custom_credentials field in the /me response.

Order fields by type

✓ accepted, ✓* required, — not used on this type; the value in parentheses is the default used when the field is not sent. Unknown fields are rejected; a typo is never silently ignored.

FieldISP / DatacenterGamingResidential / Mobile
orderType✓*✓*✓*
locationId✓*✓*—
quantity✓*✓*—
unlimitedAccess / targetUrls✓*——
protocol✓ (auto)✓ (socks5)—
highConnection✓✓✓
extraIpWhitelist✓✓—
nonSequentialIps✓✓—
highSpeed✓✓—
countryCode——✓
gbLimit——✓
username / password / gateName✓✓✓

Settings changes

Settings changes such as the service name and traffic limit spend no money and are made with a separate scope (proxies:write). They don't require an Idempotency-Key.