Skip to main content
POST
/
api
/
v2
/
order
/
exchange
/
{order_id}
Exchange Proxies
curl --request POST \
  --url https://dashboard.statproxies.com/api/v2/order/exchange/{order_id} \
  --header 'Content-Type: application/json' \
  --data '
{
  "target_product_type": "<string>"
}
'
import requests

url = "https://dashboard.statproxies.com/api/v2/order/exchange/{order_id}"

payload = { "target_product_type": "<string>" }
headers = {"Content-Type": "application/json"}

response = requests.post(url, json=payload, headers=headers)

print(response.text)
const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({target_product_type: '<string>'})
};

fetch('https://dashboard.statproxies.com/api/v2/order/exchange/{order_id}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));
<?php

$curl = curl_init();

curl_setopt_array($curl, [
CURLOPT_URL => "https://dashboard.statproxies.com/api/v2/order/exchange/{order_id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'target_product_type' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
package main

import (
"fmt"
"strings"
"net/http"
"io"
)

func main() {

url := "https://dashboard.statproxies.com/api/v2/order/exchange/{order_id}"

payload := strings.NewReader("{\n \"target_product_type\": \"<string>\"\n}")

req, _ := http.NewRequest("POST", url, payload)

req.Header.Add("Content-Type", "application/json")

res, _ := http.DefaultClient.Do(req)

defer res.Body.Close()
body, _ := io.ReadAll(res.Body)

fmt.Println(string(body))

}
HttpResponse<String> response = Unirest.post("https://dashboard.statproxies.com/api/v2/order/exchange/{order_id}")
.header("Content-Type", "application/json")
.body("{\n \"target_product_type\": \"<string>\"\n}")
.asString();
require 'uri'
require 'net/http'

url = URI("https://dashboard.statproxies.com/api/v2/order/exchange/{order_id}")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"target_product_type\": \"<string>\"\n}"

response = http.request(request)
puts response.read_body
{
  "response": "<string>",
  "message": "<string>",
  "details": {},
  "details.order_id": "<string>",
  "details.previous_count": 123,
  "details.new_count": 123,
  "details.requested_count": 123,
  "details.overlapping_ips": 123,
  "details.previous_product_type": "<string>",
  "details.product_type": "<string>",
  "details.proxy_port": 123
}
Exchange all proxies on an order — every assigned IP is released and a fresh set of the same quantity is allocated, preferring servers that did not hold your previous IPs. The endpoint also supports cross-product exchange: send a target product and the same quantity is reassigned from a different datacenter pool. Omit the body (or send {}) to exchange within the same pool with unchanged behavior.
This feature requires the proxy_exchange feature to be enabled for your organization. Without it, requests return 403 Forbidden. Contact support if you need access. This endpoint only works on active datacenter orders (orders scheduled to cancel keep exchange rights until they expire).
Rate limits. These calls trigger configuration reloads on the proxy servers, so they are limited two ways:
  • Per order: one exchange per 15 minutes (a 429 response includes retry_after_seconds).
  • Per API key: at most 10 exchange/replace calls per minute combined, on top of the general 60 req/min limit.

Request

Headers

HeaderValueRequired
AuthorizationBearer {your_api_key}Yes
Content-Typeapplication/jsonOnly for cross-product exchange

Path Parameters

order_id
string
required
The order ID for which to exchange proxies.

Body Parameters

The body is optional. Omit it (or send {}) for a same-pool exchange. To exchange into a different datacenter pool, provide the target product:
target_product_type
string
The datacenter pool to move the order into. Supported values: sprint, captcha, events, lumen. The aliases product_type and target_product_id are also accepted.

Example Request

Same-pool exchange (unchanged behavior):
curl -X POST "https://dashboard.statproxies.com/api/v2/order/exchange/sub_1rqjp3jb4g9i6pkhdwmkpjob" \
  -H "Authorization: Bearer {your_api_key}"
Cross-product exchange:
curl -X POST "https://dashboard.statproxies.com/api/v2/order/exchange/sub_1rqjp3jb4g9i6pkhdwmkpjob" \
  -H "Authorization: Bearer {your_api_key}" \
  -H "Content-Type: application/json" \
  -d '{
    "target_product_type": "captcha"
  }'
The aliases product_type and target_product_id accept the same value:
{ "product_type": "captcha" }
{ "target_product_id": "captcha" }

Response

Success Response

{
  "response": "success",
  "message": "Proxies exchanged successfully",
  "details": {
    "order_id": "sub_1rqjp3jb4g9i6pkhdwmkpjob",
    "previous_count": 10,
    "new_count": 10,
    "requested_count": 10,
    "overlapping_ips": 0,
    "previous_product_type": "sprint",
    "product_type": "captcha",
    "proxy_port": 3128
  }
}
For a same-pool exchange, previous_product_type and product_type are identical.

Response Fields

response
string
Status of the request.
message
string
Human-readable result message.
details
object
Exchange operation details.
details.order_id
string
The order ID that was processed.
details.previous_count
integer
Number of IPs before the exchange.
details.new_count
integer
Number of IPs after the exchange.
details.requested_count
integer
Number of IPs requested (based on order quantity).
details.overlapping_ips
integer
Number of IPs that are the same between old and new (should be 0 ideally).
details.previous_product_type
string
The product the order was on before the exchange. Matches product_type for a same-pool exchange.
details.product_type
string
The product the order is on after the exchange.
details.proxy_port
integer
The proxy port for the order’s current product.

Error Responses

403 Forbidden - Feature Not Enabled

{
  "response": "error",
  "message": "Proxy exchange feature is not enabled for your organization. Contact support to enable this feature."
}

404 Not Found

{
  "response": "error",
  "message": "Order not found or you don't have permission to access it"
}

400 Bad Request - Order Not Active

{
  "response": "error",
  "message": "Can only exchange proxies for active or cancelled (not yet expired) orders"
}
Orders scheduled to cancel keep exchange rights until they expire; only expired or terminated orders are refused.

400 Bad Request - Wrong Product Type

{
  "response": "error",
  "message": "Proxy exchange is only available for datacenter products"
}

429 Too Many Requests - Rate Limited

{
  "response": "error",
  "message": "Proxies for this order were exchanged recently. You can exchange again in about N minute(s).",
  "retry_after_seconds": 840
}
One exchange per order is allowed every 15 minutes. The retry_after_seconds field tells you how long to wait.

500 Internal Server Error

{
  "response": "error",
  "message": "Internal server error during proxy exchange"
}

How It Works

1

Request Exchange

You call the exchange endpoint with your order ID.
2

Current IPs Removed

All current proxy IPs are removed from the servers.
3

New IPs Assigned

Fresh IPs from the available pool are assigned to your order.
4

Confirmation

The response confirms the exchange with before/after counts.

Requirements

  • Active order: The order must be active, or cancelled but not yet expired (orders scheduled to cancel keep exchange rights until expiration)
  • Datacenter product: Exchange is only available for datacenter proxy products
  • Feature enabled: Your organization must have the proxy_exchange feature enabled
  • Within rate limits: One exchange per order every 15 minutes, and at most 10 exchange/replace calls per minute per API key

Use Cases

  • IP blocked: Get fresh IPs if your current ones are blocked
  • Clean slate: Start with unused IPs for a new project
  • Rotation: Periodic IP rotation for long-running operations

Notes

  • The exchange process takes a few seconds to complete
  • Your proxy username and password remain the same
  • The new IPs will be different from your previous ones (overlap should be 0)
  • A same-pool exchange does not affect your billing or subscription
  • To swap a single IP instead of the whole set, use Replace Proxy

Cross-product exchange safety

When moving an order to a different product, the operation is fully atomic:
  • If the target pool cannot fully assign the requested quantity, the old proxies, the order record, and the Stripe subscription are all left unchanged.
  • Stripe is updated only after the new target proxies are fully assigned.
  • Source proxies are deleted only after target assignment and the Stripe update both succeed.

Getting Access

If you need the proxy exchange feature enabled for your organization:

Contact Support

Email us to enable proxy exchange for your organization.