# Orders — PapaProxy.net API reference

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

### Place an order

`POST /api/v1/billing/composite/order`

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

Buy a proxy package (datacenter or ISP proxies) built from one or more segments (`[{country, count}]`): at least 100 IPs in total and at least 10 per country. Availability and price are re-checked at order time, and a single service is created with all segments. For dedicated proxies, use `POST /api/v1/billing/dedicated/order`.

- `whitelist_ips` must contain at least one IPv4 address. Without it the response is `422` with `detail.error = "WHITELIST_REQUIRED"`.
- With `use_balance=true`, your wallet balance is applied first. If it covers the total, the order is paid right away. Otherwise the service is created with `status: "pending"` and `payment_required: true`: `balance_applied_cents` is the part covered by your balance, and `amount_due_cents` is the remainder. Pay it in the Dashboard under Pending payments; the IPs are issued after payment.
- With `use_balance=false` (the default), the whole amount is due the same way.
- Pass `idempotency_key`: a retry with the same key returns the original order and doesn't charge again. Without a key, every request creates a new order, even with identical segments.

Request body (JSON):

| Field | Type | Required | Description |
|---|---|---|---|
| `product_id` | string (uuid), nullable | no | Optional catalog product to base the order on. When omitted, the product is selected by `product_kind`. |
| `segments` | array of object | yes | One or more segments forming the mix · Min items: 1 |
| `segments[].country` | integer or string | yes | ISO 3166-1 alpha-2 country code ("DE", "GB"). A numeric country ID from `GET /api/v1/billing/countries` is also accepted. |
| `segments[].count` | integer | yes | Number of IPs in this country. At least 10; the order total must be at least 100. · Minimum: 1 · Maximum: 100000 |
| `product_kind` | string | no | Product: `proxy_datacenter` or `proxy_isp`. Dedicated proxies are priced and ordered through `/api/v1/billing/dedicated/*`. · Default: `"proxy_datacenter"` |
| `period_months` | integer | no | Term length in months (1, 3, 6, or 12). Longer terms get a discount. Default: 1. |
| `whitelist_ips` | array of string | no | IPv4 addresses to whitelist at order time. Proxy orders need at least one; without it the request fails with HTTP 422 and nothing is charged. |
| `auto_renew` | boolean | no | Renew the service automatically when the term ends. Default: true. |
| `use_balance` | boolean | no | Apply your wallet balance first. Default: `false` — the full amount is due through a payment processor and the service stays `pending` until it's paid. |
| `geo_source` | string | no | Geo source for country lookups: maxmind (geolocation) or ripe (RIR allocation). Default maxmind. · One of: `maxmind`, `ripe` |
| `idempotency_key` | string, nullable | no | Idempotency key. A retry with the same key returns the original order without charging again. Without a key, every request creates a new order. |
| `promo_code` | string, nullable | no | Optional promo code. The discount applies to the first term only; renewals are charged at the regular price. · Max length: 64 |
| `trial` | boolean | no | Order the paid 48-hour trial instead of a full term. · Default: `false` |
| `extra_whitelist_count` | integer | no | Paid whitelist IPs beyond the free allowance. Included in the charge. · Minimum: 0 · Maximum: 10000 · Default: `0` |
| `udp_unlock` | boolean | no | Add the UDP add-on. Included in the charge. · Default: `false` |
| `extra_connections_count` | integer | no | Paid concurrent connections beyond those included. Included in the charge. For an existing service, buy the `extra_connections` add-on instead. · Minimum: 0 · Maximum: 10000 · Default: `0` |

Request examples:

cURL:

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

Python:

```python
import os

import requests

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

Node.js:

```javascript
const resp = await fetch("https://papaproxy.net/account_new/api/v1/billing/composite/order", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.PAPAPROXY_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "segments": [
      {
        "country": "US",
        "count": 100
      }
    ],
    "period_months": 1,
    "whitelist_ips": [
      "203.0.113.10"
    ],
    "use_balance": true,
    "idempotency_key": "order-2026-001"
  }),
});
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/composite/order');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PAPAPROXY_API_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'segments' => [[
            'country' => 'US',
            'count' => 100
        ]],
        'period_months' => 1,
        'whitelist_ips' => ['203.0.113.10'],
        'use_balance' => true,
        'idempotency_key' => 'order-2026-001'
    ]),
]);
$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/composite/order", strings.NewReader(`{"segments": [{"country": "US", "count": 100}], "period_months": 1, "whitelist_ips": ["203.0.113.10"], "use_balance": true, "idempotency_key": "order-2026-001"}`))
	if err != nil {
		panic(err)
	}
	req.Header.Set("Authorization", "Bearer "+os.Getenv("PAPAPROXY_API_KEY"))
	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": "dea53a24-1760-501d-a60a-d438bd176599",
  "status": "pending",
  "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
    }
  ],
  "total_ip": 100,
  "total_price_cents": 1900,
  "period_months": 1,
  "discount_pct": 0,
  "monthly_price_cents": 1900,
  "balance_applied_cents": 1777,
  "amount_due_cents": 123,
  "invoice_url": null,
  "payment_required": true,
  "intent_id": "2dbb7b45-5fb8-5bc9-834f-32aeaa569d72",
  "promo_code_applied": null,
  "promo_discount_cents": 0,
  "total_before_promo_cents": null,
  "trial": false,
  "trial_period_days": null,
  "addon_monthly_cents": 0
}
```

| Field | Type | Required | Description |
|---|---|---|---|
| `service_id` | string (uuid) | yes | Created service ID |
| `status` | string | yes | `active` when the order is paid, `pending` while `amount_due_cents` is unpaid. |
| `segments` | array of object | no | Per-segment breakdown as ordered |
| `segments[].country` | integer | yes | Numeric country ID. The ISO code is in `country_code`. |
| `segments[].country_code` | string, nullable | no | ISO 3166-1 alpha-2 country code (e.g. "DE"). |
| `segments[].count` | integer | yes |  |
| `segments[].unit_base_cents` | integer | yes | Volume-tier base unit price in integer cents per IP |
| `segments[].type_multiplier` | number | yes | Product multiplier applied to the base rate. |
| `segments[].geo_multiplier` | number | yes | Country multiplier (default 1.0). |
| `segments[].segment_price_cents` | integer | yes | Segment price: count × base rate × product multiplier × country multiplier, rounded up to whole cents. |
| `segments[].available` | boolean | yes | Whether enough IPs are available for this segment. |
| `total_ip` | integer | yes | Total number of IPs. |
| `total_price_cents` | integer | yes | Discounted total charged for the full period in integer cents |
| `period_months` | integer | no | Billing period length in months (1/3/6/12) · Default: `1` |
| `discount_pct` | integer | no | Applied period discount as a whole percentage (0/10/15/20) · Default: `0` |
| `monthly_price_cents` | integer | no | Undiscounted monthly total (sum of segment prices) in integer cents · Default: `0` |
| `balance_applied_cents` | integer | no | Amount paid from your wallet balance, in cents. Zero when `use_balance` is false. · Default: `0` |
| `amount_due_cents` | integer | no | Amount still due through a payment processor, in cents. Zero when the balance covers the order. · Default: `0` |
| `invoice_url` | string, nullable | no | Link to the invoice, if one was issued. |
| `payment_required` | boolean | no | `true` when `amount_due_cents` must still be paid. Pay it in the Dashboard under Pending payments. · Default: `false` |
| `intent_id` | string (uuid), nullable | no | ID of the pending payment when `payment_required` is `true`. |
| `promo_code_applied` | string, nullable | no | Promo code applied to the order, if any. |
| `promo_discount_cents` | integer | no | Promo discount applied to the order, in cents. · Minimum: 0 · Default: `0` |
| `total_before_promo_cents` | integer, nullable | no | Pre-promo total when a promo was applied; null otherwise. |
| `trial` | boolean | no | True when the service is a paid trial. · Default: `false` |
| `trial_period_days` | integer, nullable | no | Trial length in days; null for regular orders. |
| `addon_monthly_cents` | integer | no | Monthly cost of add-ons bought with the order, in cents. · Minimum: 0 · Default: `0` |

- `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"}
  ```

- `422` — Validation error, fewer than 100 IPs in total, or no whitelisted IP.

  Example:

  ```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"}
  ```


### Quote additional IPs

`POST /api/v1/billing/services/{service_id}/add-ips/quote`

- Access: API key required

Preview adding IPs to an active service: the prorated charge for the rest of the current term and the new renewal price. No charge and no changes to the service. Works with any valid API key.

- Proxy packages: at least 100 IPs per request and at least 10 per country, as with new orders. A country below 10 IPs is listed in `errors`.
- Dedicated proxies: from 1 IP.

Parameters:

| Field | Type | Required | Description |
|---|---|---|---|
| `service_id (path)` | string (uuid) | yes | ID of the service. |

Request body (JSON):

| Field | Type | Required | Description |
|---|---|---|---|
| `segments` | array of object | yes | One or more segments to add · Min items: 1 |
| `segments[].country` | integer or string | yes | ISO 3166-1 alpha-2 country code ("DE", "GB"). A numeric country ID from `GET /api/v1/billing/countries` is also accepted. |
| `segments[].count` | integer | yes | Number of IPs to add in this country. Proxy packages: at least 10 per country and 100 per request. Dedicated proxies: from 1. · Minimum: 1 · Maximum: 10000 |

Request examples:

cURL:

```bash
curl -X POST "https://papaproxy.net/account_new/api/v1/billing/services/3fa85f64-5717-4562-b3fc-2c963f66afa6/add-ips/quote" \
  -H "Authorization: Bearer $PAPAPROXY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"segments": [{"country": "US", "count": 100}]}'
```

Python:

```python
import os

import requests

resp = requests.post(
    "https://papaproxy.net/account_new/api/v1/billing/services/3fa85f64-5717-4562-b3fc-2c963f66afa6/add-ips/quote",
    headers={"Authorization": f"Bearer {os.environ['PAPAPROXY_API_KEY']}"},
    json={
        "segments": [
            {
                "country": "US",
                "count": 100
            }
        ]
    },
    timeout=30,
)
resp.raise_for_status()
print(resp.json())
```

Node.js:

```javascript
const resp = await fetch("https://papaproxy.net/account_new/api/v1/billing/services/3fa85f64-5717-4562-b3fc-2c963f66afa6/add-ips/quote", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.PAPAPROXY_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "segments": [
      {
        "country": "US",
        "count": 100
      }
    ]
  }),
});
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/services/3fa85f64-5717-4562-b3fc-2c963f66afa6/add-ips/quote');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PAPAPROXY_API_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'segments' => [[
            'country' => 'US',
            'count' => 100
        ]]
    ]),
]);
$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/services/3fa85f64-5717-4562-b3fc-2c963f66afa6/add-ips/quote", strings.NewReader(`{"segments": [{"country": "US", "count": 100}]}`))
	if err != nil {
		panic(err)
	}
	req.Header.Set("Authorization", "Bearer "+os.Getenv("PAPAPROXY_API_KEY"))
	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` — Non-binding preview of an add-ips top-up.

Example response:

```json
{
  "service_id": "cb532178-5b03-506a-84ea-0230ff28576b",
  "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": [],
  "added_ip_count": 100,
  "monthly_price_added_cents": 1900,
  "days_remaining": 60,
  "days_per_month": 30,
  "prorated_charge_cents": 3798,
  "current_price_cents": 9500,
  "new_renewal_price_cents": 11400
}
```

| Field | Type | Required | Description |
|---|---|---|---|
| `service_id` | string (uuid) | yes | ID of the service. |
| `segments` | array of object | no | Per-segment pricing breakdown for the added IPs |
| `segments[].country` | integer | yes | Numeric country ID. The ISO code is in `country_code`. |
| `segments[].country_code` | string, nullable | no | ISO 3166-1 alpha-2 country code (e.g. "DE"). |
| `segments[].count` | integer | yes |  |
| `segments[].unit_base_cents` | integer | yes | Volume-tier base unit price in integer cents per IP |
| `segments[].type_multiplier` | number | yes | Product multiplier applied to the base rate. |
| `segments[].geo_multiplier` | number | yes | Country multiplier (default 1.0). |
| `segments[].segment_price_cents` | integer | yes | Segment price: count × base rate × product multiplier × country multiplier, rounded up to whole cents. |
| `segments[].available` | boolean | yes | Whether enough IPs are available for this segment. |
| `errors` | array of object | no | Segments that can't be fulfilled; ordering them would fail with HTTP 409. |
| `errors[].country` | integer | yes | Numeric country ID of the segment that was left out. |
| `errors[].message` | string | yes |  |
| `added_ip_count` | integer | yes | Total IPs across the added segments |
| `monthly_price_added_cents` | integer | yes | Undiscounted full-monthly cost of the added segments |
| `days_remaining` | integer | yes | Days left in the current term. |
| `days_per_month` | integer | no | Days per month used for proration. · Default: `30` |
| `prorated_charge_cents` | integer | yes | Amount charged now for the remainder of the current period |
| `current_price_cents` | integer | yes | Service's current full-period renewal price |
| `new_renewal_price_cents` | integer | yes | Renewal price after this top-up (current + added segments' full-period cost) |

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

  Example:

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

- `404` — Service not found, or it doesn't belong to your account.

  Example:

  ```json
  {"detail": "Service 3fa85f64-5717-4562-b3fc-2c963f66afa6 not found."}
  ```

- `422` — Validation error, or fewer than 100 IPs in a proxy-package request.

  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"}
  ```


### Add IPs to a service

`POST /api/v1/billing/services/{service_id}/add-ips`

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

Add IPs to an active service — in the same country or new ones. You're charged the prorated amount for the rest of the current term, and the renewal price increases accordingly. Get the exact amount from `POST …/add-ips/quote` first.

- Proxy packages: at least 100 IPs per request and at least 10 per country. Dedicated proxies: from 1 IP.
- `idempotency_key` is required. A retry with the same key returns the original result and doesn't charge again.
- Insufficient balance returns `402` with `detail.detail = "insufficient_funds"`, `required_cents`, `balance_cents`, and `deficit_cents`. Nothing is charged.

Parameters:

| Field | Type | Required | Description |
|---|---|---|---|
| `service_id (path)` | string (uuid) | yes | ID of the service. |

Request body (JSON):

| Field | Type | Required | Description |
|---|---|---|---|
| `segments` | array of object | yes | One or more segments to add · Min items: 1 |
| `segments[].country` | integer or string | yes | ISO 3166-1 alpha-2 country code ("DE", "GB"). A numeric country ID from `GET /api/v1/billing/countries` is also accepted. |
| `segments[].count` | integer | yes | Number of IPs to add in this country. Proxy packages: at least 10 per country and 100 per request. Dedicated proxies: from 1. · Minimum: 1 · Maximum: 10000 |
| `idempotency_key` | string | yes | Client-supplied idempotency key. Must be unique per intent. · Min length: 1 |

Request examples:

cURL:

```bash
curl -X POST "https://papaproxy.net/account_new/api/v1/billing/services/3fa85f64-5717-4562-b3fc-2c963f66afa6/add-ips" \
  -H "Authorization: Bearer $PAPAPROXY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"segments": [{"country": "US", "count": 100}], "idempotency_key": "add-ips-2026-001"}'
```

Python:

```python
import os

import requests

resp = requests.post(
    "https://papaproxy.net/account_new/api/v1/billing/services/3fa85f64-5717-4562-b3fc-2c963f66afa6/add-ips",
    headers={"Authorization": f"Bearer {os.environ['PAPAPROXY_API_KEY']}"},
    json={
        "segments": [
            {
                "country": "US",
                "count": 100
            }
        ],
        "idempotency_key": "add-ips-2026-001"
    },
    timeout=30,
)
resp.raise_for_status()
print(resp.json())
```

Node.js:

```javascript
const resp = await fetch("https://papaproxy.net/account_new/api/v1/billing/services/3fa85f64-5717-4562-b3fc-2c963f66afa6/add-ips", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.PAPAPROXY_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "segments": [
      {
        "country": "US",
        "count": 100
      }
    ],
    "idempotency_key": "add-ips-2026-001"
  }),
});
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/services/3fa85f64-5717-4562-b3fc-2c963f66afa6/add-ips');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PAPAPROXY_API_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'segments' => [[
            'country' => 'US',
            'count' => 100
        ]],
        'idempotency_key' => 'add-ips-2026-001'
    ]),
]);
$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/services/3fa85f64-5717-4562-b3fc-2c963f66afa6/add-ips", strings.NewReader(`{"segments": [{"country": "US", "count": 100}], "idempotency_key": "add-ips-2026-001"}`))
	if err != nil {
		panic(err)
	}
	req.Header.Set("Authorization", "Bearer "+os.Getenv("PAPAPROXY_API_KEY"))
	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` — Result of a successful add-ips top-up.

Example response:

```json
{
  "service_id": "cb532178-5b03-506a-84ea-0230ff28576b",
  "added_ip_count": 100,
  "charged_cents": 3799,
  "ledger_entry_id": "18e0387a-1131-5ccd-a3f7-2d406d4cf5b3",
  "new_total_ip": 500,
  "new_price_cents": 9500,
  "segments": [
    {
      "count": 500,
      "country": 197148
    }
  ]
}
```

| Field | Type | Required | Description |
|---|---|---|---|
| `service_id` | string (uuid) | yes | ID of the service. |
| `added_ip_count` | integer | yes | Number of IPs added. |
| `charged_cents` | integer | yes | Prorated amount charged to the wallet |
| `ledger_entry_id` | string (uuid), nullable | no | Wallet transaction for the charge; null when nothing was charged. |
| `new_total_ip` | integer | yes | Total IP count on the service after the top-up |
| `new_price_cents` | integer | yes | Service's new full-period renewal price |
| `segments` | array of object | no | All segments of the service after the top-up. |

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

  Example:

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

- `402` — Insufficient wallet balance. Nothing is charged.

  Example:

  ```json
  {"detail": {"detail": "insufficient_funds", "required_cents": 3799, "balance_cents": 1777, "deficit_cents": 2022}}
  ```

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

  Example:

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

- `404` — Service not found, or it doesn't belong to your account.

  Example:

  ```json
  {"detail": "Service 3fa85f64-5717-4562-b3fc-2c963f66afa6 not found."}
  ```

- `422` — Validation error (for example, a missing `idempotency_key`), or fewer than 100 IPs in a proxy-package request.

  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"}
  ```

