Products
Datacenter proxies from $19/mo Rotating proxies from $25/mo ISP proxies from $33/mo Dedicated proxies from $3.50/mo UDP proxies from $5/mo Try proxies
Use cases
Data & scraping AI services Social media & messaging E-commerce & finance Media & entertainment Marketing & ads Automation & tools All use cases →
Pricing
Full pricing table All locations Money-back guarantee
Resources
Dashboard guide API reference API and MCP Setup guides FAQ Blog For business About us Affiliate program
New dashboard
English
English Русский

Catalog and pricing

6 endpoints · base URL https://papaproxy.net/account_new · authentication

List available countries

GET/api/v1/billing/countries
No API key required

List countries with their ISO 3166-1 alpha-2 codes, numeric IDs, and the number of IPs currently available for proxy packages.

No API key required. Pass the code straight to segments[].country in quote and order requests. Countries are sorted by available IPs, most first. Defaults: product_kind=proxy_datacenter, geo_source=maxmind (use the same geo_source in quote and order requests). For dedicated proxies, use GET /api/v1/billing/dedicated/countries.

Parameters

FieldTypeDescription
product_kind (query)string, nullableProduct: proxy_datacenter or proxy_isp
geo_source (query)string, nullableGeo source the country tags are scoped to: maxmind or ripe

Request example Try it in the Dashboard

cURL
curl -X GET "https://papaproxy.net/account_new/api/v1/billing/countries"
Python
import requests

resp = requests.get(
    "https://papaproxy.net/account_new/api/v1/billing/countries",
    timeout=30,
)
resp.raise_for_status()
print(resp.json())
Node.js
const resp = await fetch("https://papaproxy.net/account_new/api/v1/billing/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/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/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 you can order. · 7 fields
    Example response · JSON
    {
      "geo_source": "maxmind",
      "product_kind": "proxy_datacenter",
      "countries": [
        {
          "code": "US",
          "name": "United States",
          "tag_id": 197148,
          "available": 32452
        },
        {
          "code": "RU",
          "name": "Russia",
          "tag_id": 197146,
          "available": 15616
        }
      ]
    }
    FieldTypeDescription
    geo_source requiredstringGeo source the country list is scoped to (maxmind/ripe)
    product_kind requiredstringProduct kind the availability counts are computed for
    countriesarray of objectAvailable countries, most stock first
    countries[].code requiredstringISO 3166-1 alpha-2 code (e.g. 'DE', 'GB')
    countries[].name requiredstringCountry name (e.g. 'Germany')
    countries[].tag_id requiredintegerNumeric country ID (the ISO code is accepted everywhere this ID is).
    countries[].available requiredintegerIPs currently available in this country
  • 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 pricing terms

GET/api/v1/billing/composite/pricing-info
No API key required

Volume-discount tiers, term discounts, order minimums, included concurrent connections, and add-on prices for proxy packages — everything a calculator needs to show discount thresholds before requesting a quote.

No API key required. min_ips_per_order (100) and min_ips_per_segment (10) are the package minimums; they also apply when you add IPs to a package. Dedicated proxies start at 1 IP; price them with POST /api/v1/billing/dedicated/quote. The figures are for display only; quote and order responses are authoritative.

Request example Try it in the Dashboard

cURL
curl -X GET "https://papaproxy.net/account_new/api/v1/billing/composite/pricing-info"
Python
import requests

resp = requests.get(
    "https://papaproxy.net/account_new/api/v1/billing/composite/pricing-info",
    timeout=30,
)
resp.raise_for_status()
print(resp.json())
Node.js
const resp = await fetch("https://papaproxy.net/account_new/api/v1/billing/composite/pricing-info", {
  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/composite/pricing-info');
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/composite/pricing-info", 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 Pricing terms for display: volume-discount tiers, term discounts, order minimums, included concurrent connections, and add-on prices. · 22 fields
    Example response · JSON
    {
      "volume_tiers": [
        {
          "min_ips": 0,
          "price_millicents_per_ip": 19000,
          "label": null
        },
        {
          "min_ips": 100,
          "price_millicents_per_ip": 19000,
          "label": null
        }
      ],
      "period_discounts": {},
      "min_ips_per_segment": 10,
      "min_ips_per_order": 100,
      "conn_cap_tiers": [
        {
          "min_ips": 0,
          "max_ips": 800,
          "flat_cap": 1500,
          "multiplier": null
        },
        {
          "min_ips": 800,
          "max_ips": 5000,
          "flat_cap": null,
          "multiplier": 3
        }
      ],
      "whitelist_free_quota": 5,
      "extra_whitelist_ip_price_cents": 100,
      "free_refresh_days": 8,
      "trial": {
        "enabled": true,
        "period_days": 2,
        "tariff_coefficient": 2.0,
        "registration_window_enabled": false,
        "registration_window_days": 30,
        "max_per_month": 3
      }
    }
    FieldTypeDescription
    volume_tiers requiredarray of objectVolume-discount tiers, ascending by min_ips.
    volume_tiers[].min_ips requiredintegerTotal-IP threshold at which this tier appliesMinimum: 0
    volume_tiers[].price_millicents_per_ip requiredintegerPer-IP price in millicents (1/1000 of a cent)Minimum: 0
    volume_tiers[].labelstring, nullableHuman-readable tier label.
    period_discounts requiredobjectPeriod-months (string key) -> whole-percentage discount
    min_ips_per_segment requiredintegerMinimum IP count for a single segment to be acceptedMinimum: 1
    min_ips_per_order requiredintegerMinimum total IP count (summed across segments) for an orderMinimum: 1
    conn_cap_tiersarray of objectIncluded concurrent connections by IP count.
    conn_cap_tiers[].min_ips requiredintegerInclusive lower bound of this bucket's IP rangeMinimum: 0
    conn_cap_tiers[].max_ipsinteger, nullableExclusive upper bound; null = no upper bound (last bucket)
    conn_cap_tiers[].flat_capinteger, nullableFlat connection cap for this bucket
    conn_cap_tiers[].multiplierinteger, nullablePer-IP connection multiplier for this bucket
    whitelist_free_quotaintegerWhitelist IPs included free with each service.Default: 0
    extra_whitelist_ip_price_centsintegerMonthly price per whitelist IP beyond the free allowance, in cents.Default: 0
    free_refresh_daysintegerInterval, in days, between free scheduled IP list refreshes.Default: 0
    trialobjectPaid 48-hour trial settings: availability, length in days, and price coefficient.
    trial.enabledbooleanDefault: true
    trial.period_daysintegerTerm length in days.Minimum: 1Default: 2
    trial.tariff_coefficientnumberDefault: 2
    trial.registration_window_enabledbooleanDefault: false
    trial.registration_window_daysintegerMinimum: 1Default: 30
    trial.max_per_monthintegerMinimum: 0Default: 3
  • 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"
    }

Distribute IPs across countries

POST/api/v1/billing/composite/auto-plan
No API key required

Spread a total IP count across the countries that are currently in stock (the “Country mix” option). Optionally exclude countries with excluded_countries (ISO codes).

No API key required and no charge. total_ip must be at least 100 (the package minimum); a smaller value returns 422. Pass the returned segments to the quote endpoint for pricing and to the order endpoint to purchase.

Request body

FieldTypeDescription
total_ip requiredintegerTotal number of IPs to distribute. At least 100.Minimum: 1Maximum: 1000000
product_kindstringProduct: proxy_datacenter or proxy_isp. Dedicated proxies are priced and ordered through /api/v1/billing/dedicated/*.Default: "proxy_datacenter"
geo_sourcestringGeo source for country lookupsOne of: maxmind, ripeDefault: "maxmind"
excluded_countriesarray of integer or stringCountries to leave out, as ISO 3166-1 alpha-2 codes.

Request example Try it in the Dashboard

cURL
curl -X POST "https://papaproxy.net/account_new/api/v1/billing/composite/auto-plan" \
  -H "Content-Type: application/json" \
  -d '{"total_ip": 300}'
Python
import requests

resp = requests.post(
    "https://papaproxy.net/account_new/api/v1/billing/composite/auto-plan",
    json={
        "total_ip": 300
    },
    timeout=30,
)
resp.raise_for_status()
print(resp.json())
Node.js
const resp = await fetch("https://papaproxy.net/account_new/api/v1/billing/composite/auto-plan", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "total_ip": 300
  }),
});
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/composite/auto-plan');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'total_ip' => 300
    ]),
]);
$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/composite/auto-plan", strings.NewReader(`{"total_ip": 300}`))
	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 Proposed distribution of IPs by country. · 8 fields
    Example response · JSON
    {
      "snapshot_version": "5c7f1a22-bcbf-5cf5-845f-92443fc4d544",
      "segments": [
        {
          "country": 197148,
          "country_code": "US",
          "count": 148
        },
        {
          "country": 197146,
          "country_code": "RU",
          "count": 71
        }
      ],
      "total_ip": 300,
      "countries_used": 4,
      "subnet_diversity": null
    }
    FieldTypeDescription
    snapshot_version requiredstring (uuid)Version of the availability data used for this response.
    segmentsarray of objectProposed per-country distribution, summing to total_ip
    segments[].country requiredintegerNumeric country ID. The ISO code is in country_code.
    segments[].country_codestring, nullableISO 3166-1 alpha-2 country code (e.g. "DE").
    segments[].count requiredintegerNumber of IPs allocated to this countryMinimum: 1
    total_ip requiredintegerTotal IP count across all segments (echoes the request)
    countries_used requiredintegerNumber of distinct countries in the plan
    subnet_diversityinteger, nullableNumber of distinct /24 subnets in the proposed allocation. Available for ISP proxies only; null for other products.
  • 422 Validation error, or total_ip is below 100.
    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 price quote

POST/api/v1/billing/composite/quote
API key optional

Price a set of segments ([{country, count}]) for a proxy package and term. The quote is non-binding (binding=false).

  • The total must be at least 100 IPs; otherwise the response is 422.
  • A segment that can't be fulfilled — fewer than 10 IPs, or more IPs than are in stock — doesn't fail the request: it's listed in errors and left out of segments and the total. Always check errors before you order.
  • An invalid promo_code returns 422 (Invalid promo code.).

The API key is optional. With a key from an account that has a partner discount, the response also includes retail_* and partner_discount_* fields; otherwise those fields are null.

Pricing: the per-IP rate depends on the total IP count across all segments (volume tiers); term discounts apply on top. See GET /api/v1/billing/composite/pricing-info for the tiers.

Request body

FieldTypeDescription
segments requiredarray of objectOne or more segments forming the mixMin items: 1
segments[].country requiredinteger or stringISO 3166-1 alpha-2 country code ("DE", "GB"). A numeric country ID from GET /api/v1/billing/countries is also accepted.
segments[].count requiredintegerNumber of IPs in this country. At least 10; the order total must be at least 100.Minimum: 1Maximum: 100000
product_kindstringProduct: proxy_datacenter or proxy_isp. Dedicated proxies are priced and ordered through /api/v1/billing/dedicated/*.Default: "proxy_datacenter"
period_monthsintegerTerm length in months (1, 3, 6, or 12). Longer terms get a discount. Default: 1.
geo_sourcestringGeo source for country lookups: maxmind (geolocation) or ripe (RIR allocation). Default maxmind.One of: maxmind, ripe
promo_codestring, nullableOptional promo code, applied after volume and term discounts.Max length: 64
trialbooleanPrice the paid 48-hour trial instead of a full term.Default: false
extra_whitelist_countintegerPaid whitelist IPs beyond the free allowance.Minimum: 0Maximum: 10000Default: 0
udp_unlockbooleanInclude the UDP add-on (a percentage of the base price).Default: false
extra_connections_countintegerPaid concurrent connections beyond those included, priced into the quote. For an existing service, buy the extra_connections add-on instead.Minimum: 0Maximum: 10000Default: 0

Request example Try it in the Dashboard

cURL
curl -X POST "https://papaproxy.net/account_new/api/v1/billing/composite/quote" \
  -H "Content-Type: application/json" \
  -d '{"segments": [{"country": "US", "count": 100}], "period_months": 1}'
Python
import requests

resp = requests.post(
    "https://papaproxy.net/account_new/api/v1/billing/composite/quote",
    json={
        "segments": [
            {
                "country": "US",
                "count": 100
            }
        ],
        "period_months": 1
    },
    timeout=30,
)
resp.raise_for_status()
print(resp.json())
Node.js
const resp = await fetch("https://papaproxy.net/account_new/api/v1/billing/composite/quote", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "segments": [
      {
        "country": "US",
        "count": 100
      }
    ],
    "period_months": 1
  }),
});
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/composite/quote');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'segments' => [[
            'country' => 'US',
            'count' => 100
        ]],
        'period_months' => 1
    ]),
]);
$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/composite/quote", strings.NewReader(`{"segments": [{"country": "US", "count": 100}], "period_months": 1}`))
	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 errors for segments that were left out. · 42 fields
    Example response · JSON
    {
      "snapshot_version": "5c7f1a22-bcbf-5cf5-845f-92443fc4d544",
      "built_at": "2026-10-30T04:00:18.702558Z",
      "binding": false,
      "segments": [
        {
          "country": 197148,
          "country_code": "US",
          "count": 100,
          "unit_base_cents": 19,
          "type_multiplier": 1.0,
          "geo_multiplier": 1.0,
          "segment_price_cents": 1900,
          "available": true
        }
      ],
      "errors": [],
      "total_ip": 100,
      "total_price_cents": 1900,
      "unit_base_cents": 19,
      "period_months": 1,
      "discount_pct": 0,
      "volume_discount_pct": 0,
      "monthly_price_cents": 1900,
      "retail_total_price_cents": null,
      "retail_monthly_price_cents": null,
      "partner_discount_percent": null,
      "partner_discount_cents": null,
      "promo_code_applied": null,
      "promo_discount_cents": 0,
      "total_before_promo_cents": null,
      "trial": false,
      "trial_period_days": null,
      "trial_coefficient": null,
      "addon_monthly_cents": 0,
      "addon_breakdown": [],
      "trial_price_cents": null,
      "extra_whitelist_count": 0,
      "extra_connections_count": 0,
      "udp_unlock": false
    }
    FieldTypeDescription
    snapshot_version requiredstring (uuid)Version of the availability data used for this quote.
    built_atstring (date-time), nullableWhen the availability data was last updated (UTC).
    bindingbooleanAlways false — quotation is non-bindingDefault: false
    segmentsarray of objectPer-segment pricing breakdown
    segments[].country requiredintegerNumeric country ID. The ISO code is in country_code.
    segments[].country_codestring, nullableISO 3166-1 alpha-2 country code (e.g. "DE").
    segments[].count requiredinteger
    segments[].unit_base_cents requiredintegerVolume-tier base unit price in integer cents per IP
    segments[].type_multiplier requirednumberProduct multiplier applied to the base rate.
    segments[].geo_multiplier requirednumberCountry multiplier (default 1.0).
    segments[].segment_price_cents requiredintegerSegment price: count × base rate × product multiplier × country multiplier, rounded up to whole cents.
    segments[].available requiredbooleanWhether enough IPs are available for this segment.
    errorsarray of objectErrors for unavailable/invalid segments
    errors[].country requiredintegerNumeric country ID of the segment that was left out.
    errors[].message requiredstring
    total_ipintegerTotal IP count across all valid segmentsDefault: 0
    total_price_centsintegerDiscounted total charged for the full period in integer cents. Equals monthly_price_cents x period_months x (1 - discount).Default: 0
    unit_base_centsintegerVolume-tier base unit price applied (cents per IP)Default: 0
    period_monthsintegerBilling period length in months (1/3/6/12)Default: 1
    discount_pctintegerTerm discount as a whole percentage. The volume discount is reported in volume_discount_pct.Default: 0
    volume_discount_pctintegerVolume discount as a whole percentage, for display. Term discount is reported separately in discount_pct; both are already included in total_price_cents.Default: 0
    monthly_price_centsintegerUndiscounted monthly total (sum of segment prices) in integer centsDefault: 0
    retail_total_price_centsinteger, nullableTotal before the partner discount, in cents. Null unless a partner discount applies.
    retail_monthly_price_centsinteger, nullableMonthly price before the partner discount, in cents. Null unless a partner discount applies.
    partner_discount_percentinteger, nullablePartner discount percentage, when it applies.
    partner_discount_centsinteger, nullablePartner discount amount in cents, when it applies.
    promo_code_appliedstring, nullablePromo code applied to this quote, if any.
    promo_discount_centsintegerAbsolute promo discount in integer cents applied to total_price_cents.Minimum: 0Default: 0
    total_before_promo_centsinteger, nullablePre-promo total in integer cents when a promo was applied; null when no promo.
    trialbooleanTrue when the quote is for the paid trial.Default: false
    trial_period_daysinteger, nullableTrial length in days; null for regular quotes.
    trial_coefficientnumber, nullablePrice coefficient of the trial; null for regular quotes.
    addon_monthly_centsintegerMonthly cents from pre-purchase add-ons (whitelist + UDP + connections).Minimum: 0Default: 0
    addon_breakdownarray of objectPer-addon monthly line-items. Sum of monthly_cents equals addon_monthly_cents.
    addon_breakdown[].addon_type requiredstringAdd-on type.One of: extra_whitelist_ip, udp_unlock, extra_connections
    addon_breakdown[].quantity requiredintegerNumber of units (IPs or connections; 1 for UDP).Minimum: 0
    addon_breakdown[].unit_price_cents requiredintegerPer-unit monthly centsMinimum: 0
    addon_breakdown[].monthly_cents requiredintegerMonthly cost: quantity × unit price (UDP is a percentage of the base price).Minimum: 0
    trial_price_centsinteger, nullableTrial price in cents (before promo); null for regular quotes.
    extra_whitelist_countintegerPaid whitelist slots included in this quote.Minimum: 0Default: 0
    extra_connections_countintegerPre-purchase extra concurrent connections included in this quote.Minimum: 0Default: 0
    udp_unlockbooleanWhether UDP unlock was included in this quote.Default: false
  • 422 Validation error, fewer than 100 IPs in total, or an invalid promo code.
    Example · JSON
    {
      "detail": "Invalid promo code."
    }
  • 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"
    }

List products

GET/api/v1/customer/catalog
API key required scope catalog:read

The product catalog with your prices.

Parameters

FieldTypeDescription
offset (query)integerNumber of items to skip.Minimum: 0Default: 0
limit (query)integerPage size.Minimum: 1Maximum: 100Default: 50

Request example Try it in the Dashboard

cURL
curl -X GET "https://papaproxy.net/account_new/api/v1/customer/catalog" \
  -H "Authorization: Bearer $PAPAPROXY_API_KEY"
Python
import os

import requests

resp = requests.get(
    "https://papaproxy.net/account_new/api/v1/customer/catalog",
    headers={"Authorization": f"Bearer {os.environ['PAPAPROXY_API_KEY']}"},
    timeout=30,
)
resp.raise_for_status()
print(resp.json())
Node.js
const resp = await fetch("https://papaproxy.net/account_new/api/v1/customer/catalog", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.PAPAPROXY_API_KEY}`,
  },
});
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/customer/catalog');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PAPAPROXY_API_KEY'),
    ],
]);
$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"
)

func main() {
	req, err := http.NewRequest("GET", "https://papaproxy.net/account_new/api/v1/customer/catalog", nil)
	if err != nil {
		panic(err)
	}
	req.Header.Set("Authorization", "Bearer "+os.Getenv("PAPAPROXY_API_KEY"))
	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 Paginated product list. · 27 fields
    Example response · JSON
    {
      "products": [
        {
          "id": "5234db61-2f19-5aa0-8e64-faa72f7d4b76",
          "kind": "proxy_rotating",
          "billing_model": "metered_requests",
          "name": "Starter 2.5M",
          "description": "2.5 million requests over 30 days, about 83,000 a day. New datacenter IPv4 from a 100,000+ pool on every request; sticky sessions keep one IP when you need it. Unlimited bandwidth.",
          "params": {
            "ip_count": null,
            "geo": null,
            "protocol": null,
            "rate_limit_rps": null,
            "sticky": true,
            "data_limit_bytes": null,
            "traffic_gb": null,
            "pool_class": "datacenter",
            "devices": null,
            "period_days": 30,
            "request_package_size": 2500000
          },
          "price_cents": 4900,
          "price_unit": "period",
          "price_book_id": "1f4229e5-ac7e-50ae-bc7c-2680e501a91f",
          "is_active": true,
          "features": []
        },
        {
          "id": "79001ddf-8570-583a-9045-0511848550e4",
          "kind": "proxy_rotating",
          "billing_model": "metered_requests",
          "name": "Standard 5M",
          "description": "5 million requests over 30 days, about 167,000 a day. New datacenter IPv4 from a 100,000+ pool on every request; sticky sessions keep one IP when you need it. Unlimited bandwidth.",
          "params": {
            "ip_count": null,
            "geo": null,
            "protocol": null,
            "rate_limit_rps": null,
            "sticky": true,
            "data_limit_bytes": null,
            "traffic_gb": null,
            "pool_class": "datacenter",
            "devices": null,
            "period_days": 30,
            "request_package_size": 5000000
          },
          "price_cents": 7900,
          "price_unit": "period",
          "price_book_id": "1f4229e5-ac7e-50ae-bc7c-2680e501a91f",
          "is_active": true,
          "features": []
        }
      ],
      "total": 6,
      "offset": 0,
      "limit": 2,
      "price_book_kind": "retail"
    }
    FieldTypeDescription
    products requiredarray of object
    products[].id requiredstring (uuid)Unique ID.
    products[].kind requiredstringProduct type, for example proxy_rotating.
    products[].billing_model requiredstring
    products[].name requiredstring
    products[].description requiredstring, nullable
    products[].params requiredobject, nullableProduct parameters; they vary by product type (for example IP count, locations, term, request allowance, or session settings).
    products[].params.ip_countinteger, nullableNumber of IPs.
    products[].params.geoarray of string, nullable
    products[].params.protocolstring, nullable
    products[].params.rate_limit_rpsinteger, nullable
    products[].params.stickyboolean, nullable
    products[].params.data_limit_bytesinteger, nullableData allowance in bytes, for products with a data allowance.
    products[].params.traffic_gbnumber, nullableData allowance in GB, for products with a data allowance.
    products[].params.pool_classstring, nullableIP pool type of the product.One of: datacenter, isp
    products[].params.devicesinteger, nullable
    products[].params.period_daysinteger, nullableTerm length in days.
    products[].params.request_package_sizeinteger, nullablePrepaid request count for rotating proxies (for example 2,500,000).
    products[].price_centsinteger, nullableResolved retail price in cents; null if none
    products[].price_unitstring, nullable
    products[].price_book_idstring (uuid), nullableID of the price list the price comes from.
    products[].is_active requiredbooleanWhether it's active.
    products[].featuresarray of stringProduct features.
    total requiredintegerTotal number of items across all pages.
    offsetintegerNumber of items skipped.Default: 0
    limitintegerPage size.Default: 50
    price_book_kindstringOne of: retail, wholesaleDefault: "retail"
  • 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 catalog:read.
    Example · JSON
    {
      "detail": "API key is missing required scope: catalog:read"
    }
  • 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 product

GET/api/v1/customer/catalog/{product_id}
API key required scope catalog:read

Product details with your price.

Parameters

FieldTypeDescription
product_id (path) requiredstring (uuid)ID of the product.

Request example Try it in the Dashboard

cURL
curl -X GET "https://papaproxy.net/account_new/api/v1/customer/catalog/3fa85f64-5717-4562-b3fc-2c963f66afa6" \
  -H "Authorization: Bearer $PAPAPROXY_API_KEY"
Python
import os

import requests

resp = requests.get(
    "https://papaproxy.net/account_new/api/v1/customer/catalog/3fa85f64-5717-4562-b3fc-2c963f66afa6",
    headers={"Authorization": f"Bearer {os.environ['PAPAPROXY_API_KEY']}"},
    timeout=30,
)
resp.raise_for_status()
print(resp.json())
Node.js
const resp = await fetch("https://papaproxy.net/account_new/api/v1/customer/catalog/3fa85f64-5717-4562-b3fc-2c963f66afa6", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.PAPAPROXY_API_KEY}`,
  },
});
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/customer/catalog/3fa85f64-5717-4562-b3fc-2c963f66afa6');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PAPAPROXY_API_KEY'),
    ],
]);
$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"
)

func main() {
	req, err := http.NewRequest("GET", "https://papaproxy.net/account_new/api/v1/customer/catalog/3fa85f64-5717-4562-b3fc-2c963f66afa6", nil)
	if err != nil {
		panic(err)
	}
	req.Header.Set("Authorization", "Bearer "+os.Getenv("PAPAPROXY_API_KEY"))
	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 A catalog product. · 22 fields
    Example response · JSON
    {
      "id": "5234db61-2f19-5aa0-8e64-faa72f7d4b76",
      "kind": "proxy_rotating",
      "billing_model": "metered_requests",
      "name": "Starter 2.5M",
      "description": "2.5 million requests over 30 days, about 83,000 a day. New datacenter IPv4 from a 100,000+ pool on every request; sticky sessions keep one IP when you need it. Unlimited bandwidth.",
      "params": {
        "ip_count": null,
        "geo": null,
        "protocol": null,
        "rate_limit_rps": null,
        "sticky": true,
        "data_limit_bytes": null,
        "traffic_gb": null,
        "pool_class": "datacenter",
        "devices": null,
        "period_days": 30,
        "request_package_size": 2500000
      },
      "price_cents": 4900,
      "price_unit": "period",
      "price_book_id": "1f4229e5-ac7e-50ae-bc7c-2680e501a91f",
      "is_active": true,
      "features": []
    }
    FieldTypeDescription
    id requiredstring (uuid)Unique ID.
    kind requiredstringProduct type, for example proxy_rotating.
    billing_model requiredstring
    name requiredstring
    description requiredstring, nullable
    params requiredobject, nullableProduct parameters; they vary by product type (for example IP count, locations, term, request allowance, or session settings).
    params.ip_countinteger, nullableNumber of IPs.
    params.geoarray of string, nullable
    params.protocolstring, nullable
    params.rate_limit_rpsinteger, nullable
    params.stickyboolean, nullable
    params.data_limit_bytesinteger, nullableData allowance in bytes, for products with a data allowance.
    params.traffic_gbnumber, nullableData allowance in GB, for products with a data allowance.
    params.pool_classstring, nullableIP pool type of the product.One of: datacenter, isp
    params.devicesinteger, nullable
    params.period_daysinteger, nullableTerm length in days.
    params.request_package_sizeinteger, nullablePrepaid request count for rotating proxies (for example 2,500,000).
    price_centsinteger, nullableResolved retail price in cents; null if none
    price_unitstring, nullable
    price_book_idstring (uuid), nullableID of the price list the price comes from.
    is_active requiredbooleanWhether it's active.
    featuresarray of stringProduct features.
  • 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 catalog:read.
    Example · JSON
    {
      "detail": "API key is missing required scope: catalog:read"
    }
  • 404 Product not found, or it doesn't belong to your account. — error format
  • 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"
    }

Last updated: November 9, 2026. Generated from the OpenAPI 3.1 specification (API version 0.1.0). Markdown version for AI agents.