Dedicated proxies
3 endpoints · base URL https://papaproxy.net/account_new · authentication
List dedicated-proxy countries
GET
/api/v1/billing/dedicated/countriesNo 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 | Description |
|---|---|---|
geo_source (query) | string | Geo source for country lookups: maxmind (geolocation) or ripe (RIR allocation). Use the same value in countries, quote, and order requests.One of: maxmind, ripeDefault: "maxmind" |
product_kind (query) | string | Dedicated datacenter (proxy_datacenter) or dedicated ISP (proxy_isp) proxies.One of: proxy_datacenter, proxy_ispDefault: "proxy_datacenter" |
Request example Try it in the Dashboard
cURL
curl -X GET "https://papaproxy.net/account_new/api/v1/billing/dedicated/countries"
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
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
$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
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
200Countries with dedicated IPs in stock. · 6 fieldsExample response · JSON{ "countries": [ { "country": "IL", "available_ips": 453 } ], "geo_source": "maxmind", "product_kind": "proxy_datacenter", "offered_kinds": [ "proxy_datacenter", "proxy_isp" ] }Field Type Description countriesarray of object Countries with at least one IP available. countries[].countryrequiredstring ISO 3166-1 alpha-2 country code. countries[].available_ipsrequiredinteger Number of dedicated IPs currently available. geo_sourcestring Geo source the counts are for.One of: maxmind,ripeDefault:"maxmind"product_kindstring Product the counts are for.One of: proxy_datacenter,proxy_ispDefault:"proxy_datacenter"offered_kindsarray of string Dedicated products you can order. 422Validation error: a field is missing or has the wrong type or value.Example · JSON{ "detail": "Validation error" }429Too 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/quoteAPI 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[].availableis at leastcountbefore ordering, or the order fails with409. - The per-IP rate (
unit_millicents) depends on the total IP count; term discounts apply on top. - Pass
total_price_centsto the order asexpected_total_cents.
The API key is optional.
Request body
| Field | Type | Description |
|---|---|---|
segments required | array of object | One or more countries with IP counts.Min items: 1 |
segments[].country required | string | ISO 3166-1 alpha-2 country code ("IL", "US"). Numeric IDs aren't accepted. |
segments[].count required | integer | Number of dedicated IPs in this country. From 1.Minimum: 1 |
product_kind | string | Dedicated datacenter (proxy_datacenter) or dedicated ISP (proxy_isp) proxies.One of: proxy_datacenter, proxy_ispDefault: "proxy_datacenter" |
period_months | integer | Term in months. Longer terms get a discount: 10% for 3 months, 15% for 6, 20% for 12.One of: 1, 3, 6, 12Default: 1 |
geo_source | string | Geo source for country lookups: maxmind (geolocation) or ripe (RIR allocation). Use the same value in countries, quote, and order requests.One of: maxmind, ripeDefault: "maxmind" |
promo_code | string, nullable | Optional promo code. |
udp_unlock | boolean | Add UDP (50% of the base price).Default: false |
extra_connections_count | integer | Paid concurrent connections beyond those included.Default: 0 |
extra_whitelist_count | integer | Paid whitelisted IPs beyond the free allowance.Default: 0 |
Request example Try it in the Dashboard
cURL
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
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
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
$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
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
200Price quote. Checksegments[].availablebefore ordering. · 20 fieldsExample 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 Description unit_millicentsinteger Monthly price per IP, in millicents (thousandths of a cent). Depends on the total IP count. total_price_centsinteger Total for the whole term, in cents. Pass it as expected_total_centswhen ordering.ip_countinteger Total number of IPs. min_ips_per_orderinteger Minimum IPs per order (1 for dedicated proxies). segmentsarray of object Requested countries with current stock. segments[].countrystring ISO 3166-1 alpha-2 country code. segments[].countinteger Requested number of IPs. segments[].availableinteger IPs currently in stock. If it's lower than count, an order fails with409dedicated_out_of_stock.geo_sourcestring Geo source used.One of: maxmind,ripeDefault:"maxmind"product_kindstring Product priced.One of: proxy_datacenter,proxy_ispDefault:"proxy_datacenter"dedicated_classstring dcfor datacenter,ispfor ISP.period_monthsinteger Term in months. monthly_price_centsinteger Monthly price, including add-ons, before the term discount. segment_monthly_centsinteger Monthly price of the IPs alone. addon_monthly_centsinteger Monthly price of add-ons. discount_pctinteger Term discount, in percent. partner_discount_pctinteger, nullable Partner discount, in percent, if your account has one. promo_codestring, nullable Applied promo code. promo_discount_centsinteger Promo discount, in cents. total_before_promo_centsinteger Total before the promo discount, in cents. 422Validation error: an emptysegmentslist, acountbelow 1, a country that isn't an ISO code, or an unsupportedperiod_months.Example · JSON{ "detail": "Validation error" }429Too 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" }500Currently returned for an unrecognizedpromo_codeinstead of a validation error. Check the promo code and retry without it. — error format
Order dedicated proxies
POST
/api/v1/billing/dedicated/orderAPI key required scope
services:writeBuy dedicated proxies — IPs reserved exclusively for you — from 1 IP. Get the price from POST /api/v1/billing/dedicated/quote first.
- Send the
Idempotency-Keyheader (1–128 characters). A retry with the same key and body returns the original order and doesn't charge again (balance_applied_centsis0in the repeated response). The same key with a different body returns400. The key goes in the header, not in the body. expected_total_centsis required: passtotal_price_centsfrom the quote. If the price has changed, the response is409withdetail.code = "price_changed"and the currentactual_total_cents.- If a country doesn't have enough IPs, the response is
409withdetail.code = "dedicated_out_of_stock"and the shortfall per country. whitelist_ipsmust contain at least one IPv4 address.- With
use_balance=true, your wallet balance is applied first; if it covers the total, the service isactiveright away. Otherwise, and withuse_balance=false(the default), the service ispendinguntilamount_due_centsis paid in the Dashboard under Pending payments. - Fields that other order endpoints accept —
idempotency_key,trial,addons,expected_price_cents— are rejected here with422.
Parameters
| Field | Type | Description |
|---|---|---|
Idempotency-Key (header) required | string | Unique key for this order. Reuse it only to retry the same request.Min length: 1Max length: 128 |
Request body
| Field | Type | Description |
|---|---|---|
segments required | array of object | One or more countries with IP counts.Min items: 1 |
segments[].country required | string | ISO 3166-1 alpha-2 country code ("IL", "US"). Numeric IDs aren't accepted. |
segments[].count required | integer | Number of dedicated IPs in this country. From 1.Minimum: 1 |
expected_total_cents required | integer | 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 required | array of string | IPv4 addresses to whitelist. At least one is required.Min items: 1 |
product_kind | string | Dedicated datacenter (proxy_datacenter) or dedicated ISP (proxy_isp) proxies.One of: proxy_datacenter, proxy_ispDefault: "proxy_datacenter" |
period_months | integer | Term in months. Longer terms get a discount: 10% for 3 months, 15% for 6, 20% for 12.One of: 1, 3, 6, 12Default: 1 |
geo_source | string | Geo source for country lookups: maxmind (geolocation) or ripe (RIR allocation). Use the same value in countries, quote, and order requests.One of: maxmind, ripeDefault: "maxmind" |
use_balance | boolean | 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 | Renew automatically when the term ends. |
promo_code | string, nullable | Optional promo code. |
udp_unlock | boolean | Add UDP (50% of the base price).Default: false |
extra_connections_count | integer | Paid concurrent connections beyond those included.Default: 0 |
extra_whitelist_count | integer | Paid whitelisted IPs beyond the free allowance.Default: 0 |
Request example Try it in the Dashboard
cURL
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
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
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
$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
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
201The order was created. Checkpayment_requiredandamount_due_cents. · 10 fieldsExample 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 Description service_idstring (uuid) ID of the new service. statusstring activewhen the order is paid,pendingwhileamount_due_centsis unpaid.ip_countinteger Number of IPs ordered. total_price_centsinteger Total for the term, in cents. period_monthsinteger Term in months. monthly_price_centsinteger Monthly price, in cents. balance_applied_centsinteger Part of the total covered by your wallet balance. A retry with the same Idempotency-Keyreturns0here, because nothing is charged again.amount_due_centsinteger Amount still to be paid, in cents. payment_requiredboolean truewhenamount_due_centsmust still be paid in the Dashboard.intent_idstring (uuid), nullable ID of the pending payment, if any. 400TheIdempotency-Keywas already used with a different request body.Example · JSON{ "detail": "Idempotency-Key reused with a different dedicated order body" }401The API key is missing, malformed, revoked, or expired, or the account is inactive.Example · JSON{ "detail": "Invalid or revoked API key." }403The API key lacks scopeservices:write.Example · JSON{ "detail": "API key is missing required scope: services:write" }409The 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 } } } }422Validation error, a missingIdempotency-Keyheader orexpected_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. …" } }429Too 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" }
Last updated: November 9, 2026. Generated from the OpenAPI 3.1 specification (API version 0.1.0). Markdown version for AI agents.