# Dedicated proxies — PapaProxy.net API reference

Page: https://papaproxy.net/developers/reference/dedicated-proxies.php
Last updated: November 9, 2026
Base URL: `https://papaproxy.net/account_new`. Authentication and conventions: https://papaproxy.net/developers/reference/

### List dedicated-proxy countries

`GET /api/v1/billing/dedicated/countries`

- Access: No API key required

Countries with dedicated IPs in stock and the number of available IPs. Only countries with at least one IP are listed.

No API key required. Use the same `geo_source` and `product_kind` in quote and order requests.

Parameters:

| Field | Type | Required | Description |
|---|---|---|---|
| `geo_source (query)` | string | no | Geo source for country lookups: `maxmind` (geolocation) or `ripe` (RIR allocation). Use the same value in countries, quote, and order requests. · One of: `maxmind`, `ripe` · Default: `"maxmind"` |
| `product_kind (query)` | string | no | Dedicated datacenter (`proxy_datacenter`) or dedicated ISP (`proxy_isp`) proxies. · One of: `proxy_datacenter`, `proxy_isp` · Default: `"proxy_datacenter"` |

Request examples:

cURL:

```bash
curl -X GET "https://papaproxy.net/account_new/api/v1/billing/dedicated/countries"
```

Python:

```python
import requests

resp = requests.get(
    "https://papaproxy.net/account_new/api/v1/billing/dedicated/countries",
    timeout=30,
)
resp.raise_for_status()
print(resp.json())
```

Node.js:

```javascript
const resp = await fetch("https://papaproxy.net/account_new/api/v1/billing/dedicated/countries", {
  method: "GET",
});
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
console.log(await resp.json());
```

PHP:

```php
<?php
$ch = curl_init('https://papaproxy.net/account_new/api/v1/billing/dedicated/countries');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
var_dump($status, json_decode($response, true));
```

Go:

```go
package main

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

func main() {
	req, err := http.NewRequest("GET", "https://papaproxy.net/account_new/api/v1/billing/dedicated/countries", nil)
	if err != nil {
		panic(err)
	}
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	data, _ := io.ReadAll(resp.Body)
	fmt.Println(resp.Status, string(data))
}
```

Responses:

- `200` — Countries with dedicated IPs in stock.

Example response:

```json
{
  "countries": [
    {
      "country": "IL",
      "available_ips": 453
    }
  ],
  "geo_source": "maxmind",
  "product_kind": "proxy_datacenter",
  "offered_kinds": [
    "proxy_datacenter",
    "proxy_isp"
  ]
}
```

| Field | Type | Required | Description |
|---|---|---|---|
| `countries` | array of object | no | Countries with at least one IP available. |
| `countries[].country` | string | yes | ISO 3166-1 alpha-2 country code. |
| `countries[].available_ips` | integer | yes | Number of dedicated IPs currently available. |
| `geo_source` | string | no | Geo source the counts are for. · One of: `maxmind`, `ripe` · Default: `"maxmind"` |
| `product_kind` | string | no | Product the counts are for. · One of: `proxy_datacenter`, `proxy_isp` · Default: `"proxy_datacenter"` |
| `offered_kinds` | array of string | no | Dedicated products you can order. |

- `422` — Validation error: a field is missing or has the wrong type or value.

  Example:

  ```json
  {"detail": "Validation error"}
  ```

- `429` — Too many requests: the per-account limit is 60 requests per minute, shared by all keys and the MCP server. Retry with exponential backoff.

  Example:

  ```json
  {"detail": "Too Many Requests"}
  ```


### Get a dedicated-proxy quote

`POST /api/v1/billing/dedicated/quote`

- Access: API key optional

Price dedicated proxies: from 1 IP per order, with no per-country minimum. The quote doesn't reserve IPs.

- Countries are ISO codes only. A country that's out of stock still gets a price: check that `segments[].available` is at least `count` before ordering, or the order fails with `409`.
- The per-IP rate (`unit_millicents`) depends on the total IP count; term discounts apply on top.
- Pass `total_price_cents` to the order as `expected_total_cents`.

The API key is optional.

Request body (JSON):

| Field | Type | Required | Description |
|---|---|---|---|
| `segments` | array of object | yes | One or more countries with IP counts. · Min items: 1 |
| `segments[].country` | string | yes | ISO 3166-1 alpha-2 country code ("IL", "US"). Numeric IDs aren't accepted. |
| `segments[].count` | integer | yes | Number of dedicated IPs in this country. From 1. · Minimum: 1 |
| `product_kind` | string | no | Dedicated datacenter (`proxy_datacenter`) or dedicated ISP (`proxy_isp`) proxies. · One of: `proxy_datacenter`, `proxy_isp` · Default: `"proxy_datacenter"` |
| `period_months` | integer | no | Term in months. Longer terms get a discount: 10% for 3 months, 15% for 6, 20% for 12. · One of: `1`, `3`, `6`, `12` · Default: `1` |
| `geo_source` | string | no | Geo source for country lookups: `maxmind` (geolocation) or `ripe` (RIR allocation). Use the same value in countries, quote, and order requests. · One of: `maxmind`, `ripe` · Default: `"maxmind"` |
| `promo_code` | string, nullable | no | Optional promo code. |
| `udp_unlock` | boolean | no | Add UDP (50% of the base price). · Default: `false` |
| `extra_connections_count` | integer | no | Paid concurrent connections beyond those included. · Default: `0` |
| `extra_whitelist_count` | integer | no | Paid whitelisted IPs beyond the free allowance. · Default: `0` |

Request examples:

cURL:

```bash
curl -X POST "https://papaproxy.net/account_new/api/v1/billing/dedicated/quote" \
  -H "Content-Type: application/json" \
  -d '{"segments": [{"country": "IL", "count": 2}], "period_months": 3}'
```

Python:

```python
import requests

resp = requests.post(
    "https://papaproxy.net/account_new/api/v1/billing/dedicated/quote",
    json={
        "segments": [
            {
                "country": "IL",
                "count": 2
            }
        ],
        "period_months": 3
    },
    timeout=30,
)
resp.raise_for_status()
print(resp.json())
```

Node.js:

```javascript
const resp = await fetch("https://papaproxy.net/account_new/api/v1/billing/dedicated/quote", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "segments": [
      {
        "country": "IL",
        "count": 2
      }
    ],
    "period_months": 3
  }),
});
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
console.log(await resp.json());
```

PHP:

```php
<?php
$ch = curl_init('https://papaproxy.net/account_new/api/v1/billing/dedicated/quote');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'segments' => [[
            'country' => 'IL',
            'count' => 2
        ]],
        'period_months' => 3
    ]),
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
var_dump($status, json_decode($response, true));
```

Go:

```go
package main

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

func main() {
	req, err := http.NewRequest("POST", "https://papaproxy.net/account_new/api/v1/billing/dedicated/quote", strings.NewReader(`{"segments": [{"country": "IL", "count": 2}], "period_months": 3}`))
	if err != nil {
		panic(err)
	}
	req.Header.Set("Content-Type", "application/json")
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	data, _ := io.ReadAll(resp.Body)
	fmt.Println(resp.Status, string(data))
}
```

Responses:

- `200` — Price quote. Check `segments[].available` before ordering.

Example response:

```json
{
  "unit_millicents": 220000,
  "total_price_cents": 1188,
  "ip_count": 2,
  "min_ips_per_order": 1,
  "segments": [
    {
      "country": "IL",
      "count": 2,
      "available": 453
    }
  ],
  "geo_source": "maxmind",
  "product_kind": "proxy_datacenter",
  "dedicated_class": "dc",
  "period_months": 3,
  "monthly_price_cents": 440,
  "segment_monthly_cents": 440,
  "addon_monthly_cents": 0,
  "discount_pct": 10,
  "partner_discount_pct": null,
  "promo_code": null,
  "promo_discount_cents": 0,
  "total_before_promo_cents": 1188
}
```

| Field | Type | Required | Description |
|---|---|---|---|
| `unit_millicents` | integer | no | Monthly price per IP, in millicents (thousandths of a cent). Depends on the total IP count. |
| `total_price_cents` | integer | no | Total for the whole term, in cents. Pass it as `expected_total_cents` when ordering. |
| `ip_count` | integer | no | Total number of IPs. |
| `min_ips_per_order` | integer | no | Minimum IPs per order (1 for dedicated proxies). |
| `segments` | array of object | no | Requested countries with current stock. |
| `segments[].country` | string | no | ISO 3166-1 alpha-2 country code. |
| `segments[].count` | integer | no | Requested number of IPs. |
| `segments[].available` | integer | no | IPs currently in stock. If it's lower than `count`, an order fails with `409` `dedicated_out_of_stock`. |
| `geo_source` | string | no | Geo source used. · One of: `maxmind`, `ripe` · Default: `"maxmind"` |
| `product_kind` | string | no | Product priced. · One of: `proxy_datacenter`, `proxy_isp` · Default: `"proxy_datacenter"` |
| `dedicated_class` | string | no | `dc` for datacenter, `isp` for ISP. |
| `period_months` | integer | no | Term in months. |
| `monthly_price_cents` | integer | no | Monthly price, including add-ons, before the term discount. |
| `segment_monthly_cents` | integer | no | Monthly price of the IPs alone. |
| `addon_monthly_cents` | integer | no | Monthly price of add-ons. |
| `discount_pct` | integer | no | Term discount, in percent. |
| `partner_discount_pct` | integer, nullable | no | Partner discount, in percent, if your account has one. |
| `promo_code` | string, nullable | no | Applied promo code. |
| `promo_discount_cents` | integer | no | Promo discount, in cents. |
| `total_before_promo_cents` | integer | no | Total before the promo discount, in cents. |

- `422` — Validation error: an empty `segments` list, a `count` below 1, a country that isn't an ISO code, or an unsupported `period_months`.

  Example:

  ```json
  {"detail": "Validation error"}
  ```

- `429` — Too many requests: the per-account limit is 60 requests per minute, shared by all keys and the MCP server. Retry with exponential backoff.

  Example:

  ```json
  {"detail": "Too Many Requests"}
  ```

- `500` — Currently returned for an unrecognized `promo_code` instead of a validation error. Check the promo code and retry without it.

### Order dedicated proxies

`POST /api/v1/billing/dedicated/order`

- Access: API key required · scope `services:write`

Buy dedicated proxies — IPs reserved exclusively for you — from 1 IP. Get the price from `POST /api/v1/billing/dedicated/quote` first.

- Send the `Idempotency-Key` header (1–128 characters). A retry with the same key and body returns the original order and doesn't charge again (`balance_applied_cents` is `0` in the repeated response). The same key with a different body returns `400`. The key goes in the header, not in the body.
- `expected_total_cents` is required: pass `total_price_cents` from the quote. If the price has changed, the response is `409` with `detail.code = "price_changed"` and the current `actual_total_cents`.
- If a country doesn't have enough IPs, the response is `409` with `detail.code = "dedicated_out_of_stock"` and the shortfall per country.
- `whitelist_ips` must contain at least one IPv4 address.
- With `use_balance=true`, your wallet balance is applied first; if it covers the total, the service is `active` right away. Otherwise, and with `use_balance=false` (the default), the service is `pending` until `amount_due_cents` is paid in the Dashboard under Pending payments.
- Fields that other order endpoints accept — `idempotency_key`, `trial`, `addons`, `expected_price_cents` — are rejected here with `422`.

Parameters:

| Field | Type | Required | Description |
|---|---|---|---|
| `Idempotency-Key (header)` | string | yes | Unique key for this order. Reuse it only to retry the same request. · Min length: 1 · Max length: 128 |

Request body (JSON):

| Field | Type | Required | Description |
|---|---|---|---|
| `segments` | array of object | yes | One or more countries with IP counts. · Min items: 1 |
| `segments[].country` | string | yes | ISO 3166-1 alpha-2 country code ("IL", "US"). Numeric IDs aren't accepted. |
| `segments[].count` | integer | yes | Number of dedicated IPs in this country. From 1. · Minimum: 1 |
| `expected_total_cents` | integer | yes | `total_price_cents` from the quote you showed the user. Without it the response is `422`; if the price has changed, `409` with the current `actual_total_cents`. |
| `whitelist_ips` | array of string | yes | IPv4 addresses to whitelist. At least one is required. · Min items: 1 |
| `product_kind` | string | no | Dedicated datacenter (`proxy_datacenter`) or dedicated ISP (`proxy_isp`) proxies. · One of: `proxy_datacenter`, `proxy_isp` · Default: `"proxy_datacenter"` |
| `period_months` | integer | no | Term in months. Longer terms get a discount: 10% for 3 months, 15% for 6, 20% for 12. · One of: `1`, `3`, `6`, `12` · Default: `1` |
| `geo_source` | string | no | Geo source for country lookups: `maxmind` (geolocation) or `ripe` (RIR allocation). Use the same value in countries, quote, and order requests. · One of: `maxmind`, `ripe` · Default: `"maxmind"` |
| `use_balance` | boolean | no | Apply your wallet balance first. Default: `false` — the service is created as `pending` and the full amount is due in the Dashboard under Pending payments. |
| `auto_renew` | boolean | no | Renew automatically when the term ends. |
| `promo_code` | string, nullable | no | Optional promo code. |
| `udp_unlock` | boolean | no | Add UDP (50% of the base price). · Default: `false` |
| `extra_connections_count` | integer | no | Paid concurrent connections beyond those included. · Default: `0` |
| `extra_whitelist_count` | integer | no | Paid whitelisted IPs beyond the free allowance. · Default: `0` |

Request examples:

cURL:

```bash
curl -X POST "https://papaproxy.net/account_new/api/v1/billing/dedicated/order" \
  -H "Authorization: Bearer $PAPAPROXY_API_KEY" \
  -H "Idempotency-Key: dedicated-order-2026-001" \
  -H "Content-Type: application/json" \
  -d '{"segments": [{"country": "IL", "count": 1}], "expected_total_cents": 220, "whitelist_ips": ["203.0.113.10"], "use_balance": true}'
```

Python:

```python
import os

import requests

resp = requests.post(
    "https://papaproxy.net/account_new/api/v1/billing/dedicated/order",
    headers={
        "Authorization": f"Bearer {os.environ['PAPAPROXY_API_KEY']}",
        "Idempotency-Key": "dedicated-order-2026-001",
    },
    json={
        "segments": [
            {
                "country": "IL",
                "count": 1
            }
        ],
        "expected_total_cents": 220,
        "whitelist_ips": [
            "203.0.113.10"
        ],
        "use_balance": True
    },
    timeout=30,
)
resp.raise_for_status()
print(resp.json())
```

Node.js:

```javascript
const resp = await fetch("https://papaproxy.net/account_new/api/v1/billing/dedicated/order", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.PAPAPROXY_API_KEY}`,
    "Idempotency-Key": "dedicated-order-2026-001",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "segments": [
      {
        "country": "IL",
        "count": 1
      }
    ],
    "expected_total_cents": 220,
    "whitelist_ips": [
      "203.0.113.10"
    ],
    "use_balance": true
  }),
});
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
console.log(await resp.json());
```

PHP:

```php
<?php
$ch = curl_init('https://papaproxy.net/account_new/api/v1/billing/dedicated/order');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PAPAPROXY_API_KEY'),
        'Idempotency-Key: dedicated-order-2026-001',
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'segments' => [[
            'country' => 'IL',
            'count' => 1
        ]],
        'expected_total_cents' => 220,
        'whitelist_ips' => ['203.0.113.10'],
        'use_balance' => true
    ]),
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
var_dump($status, json_decode($response, true));
```

Go:

```go
package main

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

func main() {
	req, err := http.NewRequest("POST", "https://papaproxy.net/account_new/api/v1/billing/dedicated/order", strings.NewReader(`{"segments": [{"country": "IL", "count": 1}], "expected_total_cents": 220, "whitelist_ips": ["203.0.113.10"], "use_balance": true}`))
	if err != nil {
		panic(err)
	}
	req.Header.Set("Authorization", "Bearer "+os.Getenv("PAPAPROXY_API_KEY"))
	req.Header.Set("Idempotency-Key", "dedicated-order-2026-001")
	req.Header.Set("Content-Type", "application/json")
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	data, _ := io.ReadAll(resp.Body)
	fmt.Println(resp.Status, string(data))
}
```

Responses:

- `201` — The order was created. Check `payment_required` and `amount_due_cents`.

Example response:

```json
{
  "service_id": "1300d2ec-f511-573c-9869-93c88c501084",
  "status": "active",
  "ip_count": 1,
  "total_price_cents": 220,
  "period_months": 1,
  "monthly_price_cents": 220,
  "balance_applied_cents": 220,
  "amount_due_cents": 0,
  "payment_required": false,
  "intent_id": null
}
```

| Field | Type | Required | Description |
|---|---|---|---|
| `service_id` | string (uuid) | no | ID of the new service. |
| `status` | string | no | `active` when the order is paid, `pending` while `amount_due_cents` is unpaid. |
| `ip_count` | integer | no | Number of IPs ordered. |
| `total_price_cents` | integer | no | Total for the term, in cents. |
| `period_months` | integer | no | Term in months. |
| `monthly_price_cents` | integer | no | Monthly price, in cents. |
| `balance_applied_cents` | integer | no | Part of the total covered by your wallet balance. A retry with the same `Idempotency-Key` returns `0` here, because nothing is charged again. |
| `amount_due_cents` | integer | no | Amount still to be paid, in cents. |
| `payment_required` | boolean | no | `true` when `amount_due_cents` must still be paid in the Dashboard. |
| `intent_id` | string (uuid), nullable | no | ID of the pending payment, if any. |

- `400` — The `Idempotency-Key` was already used with a different request body.

  Example:

  ```json
  {"detail": "Idempotency-Key reused with a different dedicated order body"}
  ```

- `401` — The API key is missing, malformed, revoked, or expired, or the account is inactive.

  Example:

  ```json
  {"detail": "Invalid or revoked API key."}
  ```

- `403` — The API key lacks scope `services:write`.

  Example:

  ```json
  {"detail": "API key is missing required scope: services:write"}
  ```

- `409` — The price changed, or a country doesn't have enough IPs in stock. Nothing is charged.

  Example (price changed):

  ```json
  {"detail": {"code": "price_changed", "actual_total_cents": 220}}
  ```


  Example (out of stock):

  ```json
  {"detail": {"code": "dedicated_out_of_stock", "per_country": {"US": {"requested": 1, "available": 0}}}}
  ```

- `422` — Validation error, a missing `Idempotency-Key` header or `expected_total_cents`, or no whitelisted IP.

  Example (idempotency key required):

  ```json
  {"detail": {"code": "idempotency_key_required", "message": "Idempotency-Key header is required (1..128 chars)."}}
  ```


  Example (price confirmation required):

  ```json
  {"detail": {"code": "price_confirmation_required", "message": "expected_total_cents is required (price confirmation)."}}
  ```


  Example (whitelist required):

  ```json
  {"detail": {"error": "WHITELIST_REQUIRED", "message": "A proxy service requires at least one whitelisted IP at order time. …"}}
  ```

- `429` — Too many requests: the per-account limit is 60 requests per minute, shared by all keys and the MCP server. Retry with exponential backoff.

  Example:

  ```json
  {"detail": "Too Many Requests"}
  ```

