Getting Started with the Node4 API: First Key, First Request

Create a scoped API key, make your first call, read the JSON envelope, then list your proxies and check this month's usage from curl, Node.js or Python.

Create an API key in the dashboard, send it in an Authorization: Bearer header to https://node4.io/api/v1/profile, and a 200 with your account details means everything is wired up. From there, two more calls (GET /proxies and GET /usage) give a script your proxy list and this month's consumption. This page walks through those steps in curl, Node.js and Python. Every endpoint, field and scope is listed in the management API reference.

What you need

The API is for servers and scripts, not web pages. It sends no CORS headers, so a browser will refuse the call, and a key embedded in front-end code is public to anyone who views the source.

1. Create a key with only the scopes you need

Open Account Settings → API & Webhooks and choose Create Key. The dialog asks for three things:

  1. Key Name. Name it after the job that will use it, for example inventory-sync, so you can tell keys apart in the list later.
  2. Expiration. Never, 30, 90 or 180 days, or 1 year. A key used for a one-off experiment should expire.
  3. Permissions. Each endpoint needs exactly one scope. For this walkthrough tick read:profile, read:proxies and read:usage, and leave everything else off.

Create API Key dialog in Account Settings, API & Webhooks tab: key name, expiration and the permission scopes to grant.

The full key, starting n4k_, appears once, in the confirmation dialog. Copy it into your secret store or an environment variable straight away; Node4 keeps only a hash, so the value cannot be shown again. If you lose it, create another key and revoke the old one.

API Key Created dialog in Account Settings, API & Webhooks tab: the full key, shown only once, with a copy button.

Two scopes deserve a second thought before you tick them. read:proxies returns working proxy usernames and passwords, so a key holding it is as sensitive as the proxies themselves. write:orders can spend money. Keeping API keys secure covers storage, rotation and what to do if a key leaks.

Every example below reads the key from an environment variable:

export NODE4_API_KEY="n4k_your_key_here"

2. Make your first request

GET /profile (scope read:profile) is the cheapest way to prove the key works, because it touches nothing but your account record.

curl -s -H "Authorization: Bearer $NODE4_API_KEY" https://node4.io/api/v1/profile
// node first-call.mjs
const res = await fetch("https://node4.io/api/v1/profile", {
  headers: { Authorization: `Bearer ${process.env.NODE4_API_KEY}` },
});
console.log(res.status, await res.json());
import os
import requests

res = requests.get(
    "https://node4.io/api/v1/profile",
    headers={"Authorization": f"Bearer {os.environ['NODE4_API_KEY']}"},
    timeout=30,
)
print(res.status_code, res.json())

A working key returns 200:

{
  "success": true,
  "data": {
    "id": 364001,
    "name": "Jordan Lee",
    "email": "jordan@example.com",
    "role": "user",
    "createdAt": "2026-09-02T14:11:08.000Z"
  }
}

If your HTTP client makes custom Authorization headers awkward, X-API-Key: n4k_... is accepted instead. Send one or the other, not both.

3. Read the response envelope

Every endpoint answers in the same shape, so one helper can handle all of them.

OutcomeHTTP statusBody
Success200 (or 201 for a purchase){ "success": true, "data": { ... } }
Failure4xx or 5xx{ "error": "Human-readable reason" }
Purchase failure4xx or 5xx{ "error": "...", "code": "card_declined" }

Branch on the HTTP status first, then on code where one is present. The error text is written to be shown to a person, but it can be reworded, so do not match on it. Two first-call failures are common:

The full list of statuses and codes, and what to do with each, is in API errors, rate limits and retries.

A small wrapper keeps the rest of your code free of this plumbing. In Node.js:

const BASE = "https://node4.io/api/v1";

export async function node4(path, init = {}) {
  const res = await fetch(BASE + path, {
    ...init,
    headers: {
      Authorization: `Bearer ${process.env.NODE4_API_KEY}`,
      "Content-Type": "application/json",
      ...init.headers,
    },
  });
  const body = await res.json();
  if (!res.ok) {
    const err = new Error(`${res.status} ${body.code ?? ""} ${body.error}`.trim());
    err.status = res.status;
    err.code = body.code;
    throw err;
  }
  return body.data;
}

And in Python:

import os
import requests

BASE = "https://node4.io/api/v1"
session = requests.Session()
session.headers["Authorization"] = f"Bearer {os.environ['NODE4_API_KEY']}"


def node4(method, path, **kwargs):
    res = session.request(method, BASE + path, timeout=30, **kwargs)
    body = res.json()
    if not res.ok:
        raise RuntimeError(f"{res.status_code} {body.get('code', '')} {body.get('error')}")
    return body.get("data")

4. List your proxies

GET /proxies (scope read:proxies) returns every proxy on the account with its connection details and status:

curl -s -H "Authorization: Bearer $NODE4_API_KEY" https://node4.io/api/v1/proxies
const { total, active, proxies } = await node4("/proxies");
for (const p of proxies.filter((p) => p.status === "active")) {
  console.log(`${p.protocol}://${p.username}:${p.password}@${p.host}:${p.port}`, p.country);
}
console.log(`${active} of ${total} proxies active`);
data = node4("GET", "/proxies")
active = [p for p in data["proxies"] if p["status"] == "active"]
for p in active:
    print(f"{p['host']}:{p['port']}", p["country"], p["expiresAt"])
print(f"{data['active']} of {data['total']} proxies active")

The list includes expired and suspended proxies too, which is why the examples filter on status. Static proxies that sign in with your account credential come back with that credential filled in; an account with no active proxy credential gets empty username and password fields, because there is no login to show (authentication explains the sign-in options).

If you only need lines to paste into a scraper or proxy checker, GET /proxies/list?format=txt returns active proxies as host:port:username:password, one per line. The reference covers the other formats.

Rotating and residential plans connect through a gateway hostname rather than a list of IPs, so their connection strings come from the gateway endpoints described in the reference.

5. Check this month's usage

GET /usage (scope read:usage) reports bandwidth for the current calendar month and for the life of the account:

curl -s -H "Authorization: Bearer $NODE4_API_KEY" "https://node4.io/api/v1/usage?limit=10"
{
  "success": true,
  "data": {
    "bandwidth": {
      "uploadBytes": 1288490188,
      "downloadBytes": 20401094656,
      "totalBytes": 21689584844,
      "totalFormatted": "20.20 GB",
      "totalRequests": 418230,
      "period": "current_month"
    },
    "allTimeBandwidth": { "totalBytes": 96636764160, "totalRequests": 1904551 },
    "recentLogs": [ { "proxyId": 1201, "requestCount": 912, "bandwidthDownBytes": 48211968 } ]
  }
}

(Trimmed: the real response also carries the formatted upload and download figures and more fields per log row.)

To compare consumption against what your plan allows, add GET /subscription (scope read:subscription, which you would need to grant), whose bandwidthLimitBytes is null on unmetered plans:

used = node4("GET", "/usage")["bandwidth"]["totalBytes"]
sub = node4("GET", "/subscription")["subscription"]
limit = sub["bandwidthLimitBytes"] if sub else None
if limit:
    print(f"{used / limit:.0%} of the allowance used this month")
else:
    print(f"{used / 1024**3:.2f} GB used this month (no byte allowance)")

The usage figure covers the calendar month while /subscription reports the plan's own currentPeriodStart and currentPeriodEnd, so treat the percentage as a guide when your billing period does not start on the 1st.

For history rather than a running total, GET /usage/bandwidth?days=30 returns hourly snapshots; the usage section of the reference lists the per-proxy, hourly and daily breakdowns. If the month is running out of allowance, bandwidth top-ups add more without changing your plan.

Limits that apply

If something does not work

Where to go next