Automating Your Proxies with the Management REST API

Script the dashboard with an n4k_ key: pull credentials, meter bandwidth, edit the whitelist, catch webhooks. Plus the limits and 429s that bite.

Everything you can click on the dashboard, an integration eventually wants to do without you. Pull the current proxy list into a scraper at startup. Check bandwidth consumption from a cron job before it becomes an overage. Add a new server's address to the IP whitelist the moment it boots. This guide walks the public REST API end to end, with the request and response shapes as they actually are, and the operational limits (pagination caps, the rate limiter, permission scopes) that integrations tend to discover the hard way.

The base URL for every call is https://node4.io/api/v1. There is no separate API hostname; the API is mounted on the main domain.

Keys, headers and scopes

Create a key under Settings → API Keys on the dashboard. Keys are prefixed n4k_ and shown once at creation: the server stores only a hash, so a lost key is replaced, not recovered. Send it on every request in either header form:

curl -H "Authorization: Bearer n4k_YOUR_KEY" https://node4.io/api/v1/profile
# or
curl -H "X-API-Key: n4k_YOUR_KEY" https://node4.io/api/v1/profile

A key carries an explicit set of permission scopes, chosen when you create it: read:proxies, write:proxies, read:usage, read:subscription, read:profile, write:ip-whitelist, read:orders, read:notifications and write:webhooks. Calling an endpoint the key is not scoped for returns a 403 whose error string names the missing permission, so a scope failure is always distinguishable from a bad key (401) or a revoked/expired one (also 403, with its own message).

Scope keys narrowly. A monitoring job needs read:usage and nothing else; the key baked into a provisioning script needs write:ip-whitelist and should not also be able to read proxy credentials. Since credentials for every proxy on the account flow through read:proxies, treat any key holding that scope as secret material of the same grade as the proxies themselves; API key security covers storage and rotation practice.

One scope quirk to know before it costs you a debugging session: reading the IP whitelist requires read:profile, while adding and deleting entries require write:ip-whitelist. A key with only the write scope can modify the list but not read it back.

Listing your proxies

Two endpoints serve different consumers. GET /proxies is the complete inventory (every proxy on the account, active or not, with credentials attached):

curl -H "Authorization: Bearer n4k_YOUR_KEY" https://node4.io/api/v1/proxies
{
  "success": true,
  "data": {
    "total": 5,
    "active": 4,
    "proxies": [
      {
        "id": 1201,
        "type": "shared",
        "protocol": "http",
        "host": "…",
        "port": 4443,
        "username": "…",
        "password": "…",
        "status": "active",
        "country": "US",
        "expiresAt": "2026-09-01T00:00:00.000Z"
      }
    ]
  }
}

Note that total and active can differ: expired and suspended proxies are included so your tooling can see why capacity dropped rather than watching rows silently vanish. Filter on status yourself. The protocol field tells you which listener each row describes; if a row says socks5 and your client stack has only ever spoken HTTP, the SOCKS5 guide explains what changes.

GET /proxies/list is the compact form, built for feeding other software. With ?format=txt it returns plain text, one active proxy per line as host:port:username:password, the format most scraping tools and proxy testers import directly:

curl -H "Authorization: Bearer n4k_YOUR_KEY" "https://node4.io/api/v1/proxies/list?format=txt" > proxies.txt

Without the parameter it returns the same active-only set as a JSON array. Use /proxies/list in tooling pipelines and /proxies when you need statuses, IDs (required for refreshes, below) or expiry dates.

Reading usage and bandwidth

GET /usage returns three things in one payload: the current calendar month's bandwidth (up, down, total, request count), all-time totals, and a page of recent usage log rows. The log page size is controlled by ?limit=, defaulting to 50 with a hard ceiling of 200. Ask for more and you silently get 200, so never treat "rows returned < limit requested" as proof you have reached the end.

GET /usage/bandwidth is the time-series view: hourly snapshots over a trailing window set by ?days=, defaulting to 7 and capped at 90.

curl -H "Authorization: Bearer n4k_YOUR_KEY" "https://node4.io/api/v1/usage/bandwidth?days=30"

Each snapshot carries an hour timestamp, bytes moved and request count, enough to chart consumption or to detect that a runaway job started eating the allowance at 3 a.m. There is also GET /usage/analytics for aggregated breakdowns, and GET /subscription for the governing plan and its period dates, which together answer the practical question "will this month's usage fit?". When the answer is no, bandwidth top-ups can be applied without changing plan. Both usage endpoints need read:usage; /subscription needs read:subscription.

Managing the IP whitelist

Whitelisting is the credential-free authentication mode on every product: connections from a registered address are recognized without a username or password. It applies to the residential gateway too, where a whitelisted connection sends nothing at all and gets an untargeted exit. That makes the whitelist exactly the thing you want to automate: servers come and go, and every stale entry is either a lockout (your new box) or a liability (whoever inherits your old address). The reasoning about hygiene lives in IP whitelisting best practices; the mechanics are three calls.

List current entries (scope: read:profile):

curl -H "Authorization: Bearer n4k_YOUR_KEY" https://node4.io/api/v1/ip-whitelist

Add one (scope: write:ip-whitelist):

curl -X POST -H "Authorization: Bearer n4k_YOUR_KEY" -H "Content-Type: application/json" \
  -d '{"ipAddress": "203.0.113.7", "label": "scraper-03"}' \
  https://node4.io/api/v1/ip-whitelist

The address must be a single, publicly-routable IP. CIDR ranges are rejected, as are private, loopback and link-local addresses, because registering 10.0.0.5 would authenticate nobody. Duplicates and over-limit additions come back as 400 with a human-readable error; your account's entry limit depends on tier plus any purchased slots, and the error text states the number in force. The optional label is for you: future-you will not remember what an unlabeled address was.

Remove an entry by the id the GET returned:

curl -X DELETE -H "Authorization: Bearer n4k_YOUR_KEY" https://node4.io/api/v1/ip-whitelist/42

A sensible boot-time pattern for elastic infrastructure: fetch the machine's public address, POST it with a hostname label, and on graceful shutdown DELETE it. That keeps the list mirroring reality with no human in the loop.

Refreshing a proxy's IP

POST /proxies/refresh (scope: write:proxies) swaps a static proxy's address for a fresh one from the pool:

curl -X POST -H "Authorization: Bearer n4k_YOUR_KEY" -H "Content-Type: application/json" \
  -d '{"proxyId": 1201}' https://node4.io/api/v1/proxies/refresh

The proxyId is the numeric id from GET /proxies, and the proxy must be yours and active. Refreshes are a metered monthly allowance, not a free action: the success response reports refreshesUsed against refreshLimit, a 400 tells you the month's allowance is spent, and a 403 tells you the plan has no refresh allowance at all (the free tier does not). Two constraints worth designing around: gateway-style products have nothing to refresh (their exits change by design, as described in sticky versus rotating sessions), and a refresh invalidates the old address immediately, so anything holding open connections through it should expect them to drop.

Registering webhooks

Polling /usage every minute burns rate limit to learn mostly nothing. Webhooks invert it: the platform calls you when something happens. All three webhook endpoints use the write:webhooks scope.

curl -X POST -H "Authorization: Bearer n4k_YOUR_KEY" -H "Content-Type: application/json" \
  -d '{
    "name": "ops-alerts",
    "url": "https://ops.example.com/hooks/node4",
    "events": ["bandwidth_threshold.warning", "bandwidth_threshold.exceeded", "payment.failed"]
  }' https://node4.io/api/v1/webhooks

The event vocabulary is fixed, and an unknown event name is a 400 listing the valid set: bandwidth_threshold.warning (80% of the plan's bandwidth), bandwidth_threshold.exceeded (100%), proxy.status_change, subscription.created, subscription.canceled, subscription.renewed, payment.succeeded and payment.failed. The bandwidth pair plus payment.failed is the minimal set that keeps a production integration from being surprised.

The creation response includes a secret, returned this once only. Every delivery is signed with it (HMAC-SHA256 of the payload), and verifying that signature is not optional: your endpoint is a public URL, and without the check anyone who finds it can feed your systems fabricated events. Endpoints must be public http(s) URLs; anything pointing at internal or non-HTTP destinations is rejected at registration. Accounts are capped at 10 webhooks, and GET /webhooks / DELETE /webhooks/:id complete the lifecycle.

Rate limits, 429s and Retry-After

Every key gets 60 requests per minute. The limit is flat, the same on every plan, and it is bucketed per key, which is itself a design lever: give each subsystem its own key and a runaway retry loop in one of them cannot starve the others.

Every response carries the standard accounting headers: X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (a Unix timestamp for the window reset). When the bucket is empty you get a 429 with a Retry-After header (in seconds) and a body that repeats the numbers machine-readably:

{
  "error": "Rate limit exceeded. Please wait before making more requests.",
  "retryAfterSeconds": 42,
  "limit": 60,
  "windowSeconds": 60
}

Handle it by sleeping for Retry-After and retrying, not by hammering, which keeps the window resetting against you. Better still, watch X-RateLimit-Remaining on the way and slow down before the 429; a well-behaved poller never sees one.

The other paging endpoints

GET /orders (scope: read:orders) is the one endpoint with true offset pagination: ?limit= (default 20, ceiling 100) and ?offset= together walk the full order history, which is useful for reconciling against your own accounting; pair it with understanding your invoice when the line items need interpreting. GET /notifications (scope: read:notifications) takes the same limit with the same ceiling. All the caps clamp silently rather than erroring, so the safe iteration pattern everywhere is: request your page size, and only stop when a page comes back empty, never when it merely comes back short of the maximum you asked for, since what you asked for may not be what the server was willing to give.

Where to go from here

A reasonable first integration, in order: verify the key with GET /profile, snapshot GET /proxies/list into your tool's config, wire the three bandwidth webhook events, and put a daily GET /usage in cron as the belt to the webhook braces. Which endpoints matter most depends on the product shape: a fleet of static datacenter proxies leans on refresh and the whitelist, while gateway plans mostly need usage and webhooks. The full product line-up, and what each plan's API-visible allowances are, is on the features page.