> ## Documentation Index
> Fetch the complete documentation index at: https://docs.proxyscrape.com/llms.txt
> Use this file to discover all available pages before exploring further.

# API ordering

> Place wallet-paid orders and read your wallet balance programmatically. No gateway redirect, no checkout UI.

The **API ordering** endpoints let you place orders from your own code, paid directly from your account's wallet balance. Same product catalogue and the same request shape as the marketing checkout — minus the gateway round-trip.

<Note>
  Both endpoints are **admin-grant-only**. They're off by default for every account. Contact support to enable `account:api_ordering:write` (place orders) and/or `account:api_ordering:read` (read wallet balance).
</Note>

## Surface

| Endpoint                                  | Permission                   | What it does                                                                 |
| ----------------------------------------- | ---------------------------- | ---------------------------------------------------------------------------- |
| `POST /v4/account/orders/create`          | `account:api_ordering:write` | Place a wallet-paid order.                                                   |
| `GET /v4/account/orders/{orderId}/status` | `account:api_ordering:read`  | Poll an order's lifecycle and grab the new subaccount once processed.        |
| `GET /v4/account/orders/balance`          | `account:api_ordering:read`  | Read the wallet balance (+ optional activity counts via `include_counts=1`). |

Both accept the same auth form as the rest of the Account API — see [Authentication](/api/account/authentication):

* `api-token: <your-api-key>` — send your API key in the `api-token` header.

## Place an order

The server forces `pay_with_credit=1` and `no_redirect=1` server-side — there's no gateway redirect, the wallet is debited atomically right after the order row is created, and the call returns the new order ID.

`email`, `fname`, and `lname` are derived from the authenticated identity. Omit them, or pass them explicitly to override.

### Per-product request shapes

Each `type` accepts a different set of product-specific fields, identical to what the marketing checkout submits.

<CodeGroup>
  ```bash Premium (datacenter shared) theme={null}
  curl -X POST 'https://api.proxyscrape.com/v4/account/orders/create' \
    -H 'Content-Type: application/json' \
    -H 'api-token: <your-api-key>' \
    -d '{
      "type": "new_datacenter_shared",
      "country": "Belgium",
      "is_subscription": true,
      "days": 30,
      "proxies[mixed]": 1000,
      "bandwidth": 0
    }'
  ```

  ```bash Residential theme={null}
  curl -X POST 'https://api.proxyscrape.com/v4/account/orders/create' \
    -H 'Content-Type: application/json' \
    -H 'api-token: <your-api-key>' \
    -d '{
      "type": "new_residential",
      "country": "Belgium",
      "bandwidth": 10,
      "duration": "30d"
    }'
  ```

  ```bash Residential unlimited theme={null}
  curl -X POST 'https://api.proxyscrape.com/v4/account/orders/create' \
    -H 'Content-Type: application/json' \
    -H 'api-token: <your-api-key>' \
    -d '{
      "type": "new_residential_unlimited",
      "country": "Belgium",
      "location": "<server-location-id>",
      "speed": 200,
      "days": 30
    }'
  ```

  ```bash SERP API theme={null}
  curl -X POST 'https://api.proxyscrape.com/v4/account/orders/create' \
    -H 'Content-Type: application/json' \
    -H 'api-token: <your-api-key>' \
    -d '{
      "type": "new_serp_api",
      "country": "Belgium",
      "tokens": 100000,
      "duration": "30d"
    }'
  ```
</CodeGroup>

**Field notes**

* `country` — Full country name (e.g. `"Belgium"`), not the ISO code. VAT is derived from it.
* `proxies[<location>]` — Datacenter only. The location is one of `mixed`, `us`, `gb`, `de`, `fr`, `ca`, `es`, `it`, `br`, `th`. Value is the IP-pool size.
* `bandwidth` — Datacenter: `0` means unlimited. Residential: bandwidth pack in GB.
* `duration` — Omit for non-expiry residential / SERP packages. Otherwise `30d` / `60d` / `90d`.
* `location` — Residential unlimited only. Server-location ID; list them via the dashboard's Residential Unlimited page.
* `coupon_code` (optional) — Applied before any per-account API-ordering percentage discount.
* `account_id` (`update_*` only) — The `AccountID` of the subaccount to upgrade in place. Must be one you own. See [Upgrade an existing subaccount](#upgrade-an-existing-subaccount).
* `dry_run` (optional) — `true` returns a price quote without charging. See [Price quote (dry run)](#price-quote-dry-run).

### Response

For wallet-credit orders the `data` field is the bare order ID string:

```json theme={null}
{ "success": true, "data": "0bc1a40f-2a17-4c84-9b2b-f4b9d3e0b3a1" }
```

On failure:

```json theme={null}
{ "success": false, "error": "Missing or invalid pricing configuration." }
```

Common errors:

| Status | Error                                                                              | When                                                                            |
| ------ | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| 400    | `Missing or invalid pricing configuration.`                                        | The `type` + parameter combo doesn't match a buyable package.                   |
| 400    | `Insufficient wallet balance.`                                                     | Wallet doesn't cover the order total at debit time.                             |
| 400    | `account_id is required for update_* order types.`                                 | An `update_*` type was sent without an `account_id`.                            |
| 401    | `Could not determine account email from authenticated identity.`                   | API token is missing or invalid.                                                |
| 403    | `API ordering is not enabled for this account. Contact support to request access.` | Account doesn't have `account:api_ordering:write`.                              |
| 403    | `The account_id does not belong to the authenticated account.`                     | An `update_*` request named an `account_id` that isn't in your subaccount list. |

## Upgrade an existing subaccount

To modify a subaccount you already own — more proxies/tokens, more bandwidth, or extended duration — use the matching `update_*` type and pass the target subaccount's `account_id`. The order is paid from your wallet just like a new purchase; the difference is it modifies the existing subaccount in place instead of provisioning a new one.

`account_id` is the `AccountID` returned by `/v4/account/subaccounts` (or by the order-status `subaccount.AccountID` when the subaccount was first created via the API).

<CodeGroup>
  ```bash Premium (datacenter shared) — bump 1,000 → 2,000 proxies theme={null}
  curl -X POST 'https://api.proxyscrape.com/v4/account/orders/create' \
    -H 'Content-Type: application/json' \
    -H 'api-token: <your-api-key>' \
    -d '{
      "type": "update_datacenter_shared",
      "account_id": "fe7941e6-1eed-4d95-881a-3159901897d3",
      "days": 1,
      "proxies[mixed]": 2000,
      "bandwidth": 250,
      "is_subscription": false
    }'
  ```

  ```bash SERP API — top up tokens theme={null}
  curl -X POST 'https://api.proxyscrape.com/v4/account/orders/create' \
    -H 'Content-Type: application/json' \
    -H 'api-token: <your-api-key>' \
    -d '{
      "type": "update_serp_api",
      "account_id": "fe7941e6-1eed-4d95-881a-3159901897d3",
      "tokens": 200000
    }'
  ```
</CodeGroup>

<Note>
  `account_id` **must** be a subaccount that belongs to the authenticated account — it has to appear in your `/v4/account/subaccounts` list. A request naming an `account_id` you don't own is rejected with `403`. Omitting `account_id` on an `update_*` type is an error; to create a brand-new subaccount, use the corresponding `new_*` type instead.
</Note>

Available upgrade types: `update_datacenter_shared`, `update_datacenter_dedicated`, `update_residential`, `update_residential_unlimited`, `update_serp_api`. The product-specific fields are the same as the matching `new_*` type. Downgrades (fewer proxies/tokens than the current plan) are not supported over the API.

## Price quote (dry run)

Send `dry_run: true` alongside any order request — `new_*` or `update_*` — to get the computed price **without creating an order or debiting your wallet**. The request shape is otherwise identical to the order you'd place, so you can quote and then submit by flipping a single flag.

```bash theme={null}
curl -X POST 'https://api.proxyscrape.com/v4/account/orders/create' \
  -H 'Content-Type: application/json' \
  -H 'api-token: <your-api-key>' \
  -d '{
    "type": "update_datacenter_shared",
    "account_id": "fe7941e6-1eed-4d95-881a-3159901897d3",
    "days": 1,
    "proxies[mixed]": 2000,
    "bandwidth": 250,
    "is_subscription": false,
    "dry_run": true
  }'
```

The `data` field carries the quote rather than an order ID:

```json theme={null}
{
  "success": true,
  "data": {
    "title": "Update ProxyScrape premium",
    "description": " and 1 day extra.",
    "original_price": "26.10",
    "price": "13.05",
    "extra_days": 1,
    "discount": { "...": "present only if an API-ordering discount applies" },
    "coupon":   { "...": "present only if a coupon_code was supplied" }
  }
}
```

* `price` is the amount that will be debited from your wallet (pre-VAT; for `update_*` it's already net of credit for unused time on the current plan).
* `original_price` is the price before any coupon / per-account discount.
* A dry run works on **zero wallet balance** — it never reaches the balance check, so use it to preview cost before funding.
* A dry run requires the same `account:api_ordering:write` permission as a real order (it's the same endpoint).

## Check order status

`POST /v4/account/orders/create` returns the order ID synchronously, but the **order processor** that turns the order into a usable subaccount runs asynchronously. For wallet-paid orders this typically takes well under a second, but it's not guaranteed.

Poll `GET /v4/account/orders/{orderId}/status` every 1–2 seconds until `status === "processed"`. At that point `data.subaccount` is populated with the same shape as `/v4/account/subaccounts` entries — you can drop it straight into per-product endpoints.

```bash theme={null}
curl -X GET 'https://api.proxyscrape.com/v4/account/orders/0bc1a40f-2a17-4c84-9b2b-f4b9d3e0b3a1/status' \
  -H 'api-token: <your-api-key>'
```

**While processing:**

```json theme={null}
{
  "success": true,
  "data": {
    "order_id": "0bc1a40f-2a17-4c84-9b2b-f4b9d3e0b3a1",
    "status": "paid",
    "type": "new_datacenter_shared",
    "created_at": 1717000000,
    "updated_at": 1717000000,
    "price": "26.10",
    "currency": "USD",
    "paid_with_credit": true,
    "subaccount": null
  }
}
```

**Once processed:**

```json theme={null}
{
  "success": true,
  "data": {
    "order_id": "0bc1a40f-2a17-4c84-9b2b-f4b9d3e0b3a1",
    "status": "processed",
    "type": "new_datacenter_shared",
    "created_at": 1717000000,
    "updated_at": 1717000010,
    "price": "26.10",
    "currency": "USD",
    "paid_with_credit": true,
    "subaccount": {
      "AccountID": "9e4d49b0-c999-46f6-aa98-47476a6aef0b",
      "AccountType": "datacenter_shared",
      "label": "Account 8 (Premium)",
      "time_added": 1717000010
    }
  }
}
```

The `AccountID` is the value you pass as `{subAccountId}` to the product endpoints:

```
GET /v4/account/9e4d49b0-c999-46f6-aa98-47476a6aef0b/datacenter_shared/proxy-list
```

### Sample polling loop

```python theme={null}
import time
import requests

def wait_for_subaccount(order_id, api_token, *, timeout=30, interval=1.5):
    headers = {'api-token': api_token}
    deadline = time.monotonic() + timeout
    while time.monotonic() < deadline:
        r = requests.get(
            f'https://api.proxyscrape.com/v4/account/orders/{order_id}/status',
            headers=headers,
            timeout=10,
        )
        r.raise_for_status()
        data = r.json()['data']
        if data['status'] == 'processed':
            return data['subaccount']
        if data['status'] in ('blocked', 'failed', 'refunded'):
            raise RuntimeError(f"order rejected: {data.get('block_reason') or data['status']}")
        time.sleep(interval)
    raise TimeoutError(f'order {order_id} did not process within {timeout}s')
```

### Statuses

| Status      | Meaning                                                                                 |
| ----------- | --------------------------------------------------------------------------------------- |
| `paid`      | Wallet was debited; the processor will pick the order up shortly. Keep polling.         |
| `processed` | Subaccount has been created. `data.subaccount` is populated. Stop polling.              |
| `blocked`   | Rejected by the fraud / pricing guards. `data.block_reason` carries the specific cause. |
| `failed`    | Processing failed. Stop polling and surface support contact.                            |
| `refunded`  | Order was refunded (admin action). The subaccount is no longer valid.                   |

### Errors

| Status | Error                                                                                                                    | When                                                                                                                                                        |
| ------ | ------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 401    | `Could not determine account email from authenticated identity.`                                                         | API token is missing or invalid.                                                                                                                            |
| 403    | `Reading order status via the API-ordering endpoint is not enabled for this account. Contact support to request access.` | Account doesn't have `account:api_ordering:read`.                                                                                                           |
| 404    | `Order not found.`                                                                                                       | Either the ID doesn't exist, or it belongs to a different account. The endpoint deliberately does **not** distinguish the two so existence can't be probed. |

## Read wallet balance

```bash theme={null}
curl -X GET 'https://api.proxyscrape.com/v4/account/orders/balance' \
  -H 'api-token: <your-api-key>'
```

```json theme={null}
{
  "success": true,
  "data": {
    "balance": 42.50,
    "currency": "USD"
  }
}
```

Pass `include_counts=1` when you also need activity counts:

```bash theme={null}
curl -X GET 'https://api.proxyscrape.com/v4/account/orders/balance?include_counts=1' \
  -H 'api-token: <your-api-key>'
```

```json theme={null}
{
  "success": true,
  "data": {
    "balance": 42.50,
    "currency": "USD",
    "topups_count": 8,
    "purchases_count": 30
  }
}
```

| Field             | Description                                                                                            |
| ----------------- | ------------------------------------------------------------------------------------------------------ |
| `balance`         | Current wallet credit.                                                                                 |
| `currency`        | Always `USD`.                                                                                          |
| `topups_count`    | Number of processed wallet top-up orders. Only present when `include_counts=1`.                        |
| `purchases_count` | Number of wallet-credit purchases, excluding cancelled/refunded. Only present when `include_counts=1`. |

The endpoint scopes the read to the authenticated identity — a token can only ever read its own wallet.

## Coupons and discounts

You can pass a `coupon_code` field on `POST /v4/account/orders/create`. The order then applies, in this order:

1. **Coupon code** (if any) — percent or fixed amount off the product price.
2. **Per-account API-ordering percentage** (if your account has one) — applied on top of the post-coupon price.

Coupons cannot be applied to wallet top-ups; the server rejects the request.

If your account has an active API-ordering discount, it shows up in the dashboard playground banner and in the `me` endpoint's `api_ordering_discount` field. The percentage is applied automatically — no flag to set.

## Try it in the dashboard

The dashboard hosts a live playground for both endpoints at **[/account/api-orders](https://dashboard.proxyscrape.com/v2/account/api-orders)**. It builds the request body interactively, renders cURL / Python / Node snippets, and runs the request against your real wallet so you can verify everything works end-to-end before wiring it into your code.

## Permissions

These two permissions are documented in the [permission reference](/api/account/permission-reference#api-ordering-permissions-3-part-accountapi_orderingaction):

* `account:api_ordering:write` — required for `POST /v4/account/orders/create`.
* `account:api_ordering:read` — required for `GET /v4/account/orders/balance`.

Both are admin-grant-only and don't appear in the standard permissions picker. To enable them on your account, [contact support](mailto:support@proxyscrape.com).
