The Nodetonet REST API is plain JSON over HTTPS authenticated with a Bearer token. There is no proprietary SDK to install — you can drive the entire platform from Python with the standard requests library. That means create, configure and destroy mobile proxies, set geo-targeting modifiers, manage rotating token groups, and handle quota, expiry and per-client auth — all from your own automation scripts.
This guide starts with raw one-shot functions for every CRUD operation, then folds them into a small NodetonetClient class you can drop into any project. By the end you will have a single file that handles pagination, rate-limiting and session reuse without any extra dependencies.
Prerequisite: you have copied your personal API key (covered in understanding your API key) and stored it in an environment variable rather than hard-coding it.
export NTN_KEY="ntn_live_a8c3...f2e1"
1. The minimum viable call — list proxies
Every Nodetonet API call follows the same shape: a method, a path under https://nodetonet.com/api/v1, an Authorization: Bearer header, and an optional JSON body. The response is always {data: ..., meta: ...}.
import os
import requests
KEY = os.environ['NTN_KEY']
BASE = 'https://nodetonet.com/api/v1'
r = requests.get(
f'{BASE}/proxies',
headers={'Authorization': f'Bearer {KEY}'},
timeout=10,
)
r.raise_for_status()
for p in r.json()['data']:
print(p['id'], p['protocol'], p['host'], p['port'])
That covers the full client surface — every other endpoint is a variation on those four lines.
2. Create a proxy with geo-targeting
POST to /proxies with a JSON body. The required fields are protocol, tokenId and serverId (the edge server to run the proxy). Everything else — port, title, auth credentials — has sensible defaults.
To apply geo-targeting or carrier selection you pass username modifiers the same way a connecting client would, but here you specify them at creation time so every downstream client gets the right exit automatically:
payload = {
'title': 'scraper-tr-turkcell-1',
'protocol': 'http', # or 'socks5' — see /features/socks5-http-proxies
'port': 'auto',
'tokenId': 'tok_3kzl8w9',
'serverId': 'srv_de_1',
# optional: force a carrier/country exit at the token level
'usernameModifier': '-country-tr', # route through a Turkish exit
}
r = requests.post(
f'{BASE}/proxies',
headers={'Authorization': f'Bearer {KEY}'},
json=payload,
timeout=15,
)
r.raise_for_status()
proxy = r.json()['data']
print('created', proxy['id'], '->',
f"http://{proxy['username']}:{proxy['password']}@{proxy['host']}:{proxy['port']}")
The response includes the freshly allocated username, password, host and port — everything you need to build a connection string and start routing traffic immediately. For SOCKS5 replace http:// with socks5h:// in your downstream client.
3. Update and delete
PATCH takes a partial payload — only the fields you want to change. DELETE is permanent.
# rename a proxy
requests.patch(
f'{BASE}/proxies/{proxy_id}',
headers={'Authorization': f'Bearer {KEY}'},
json={'title': 'scraper-tr-renamed'},
timeout=10,
).raise_for_status()
# tear it down
requests.delete(
f'{BASE}/proxies/{proxy_id}',
headers={'Authorization': f'Bearer {KEY}'},
timeout=10,
).raise_for_status()
After a DELETE the proxy is removed from every edge node within a few seconds and the port returns to the shared pool. Any active client connection through it receives a clean TCP close.
4. A reusable client class
Once you have pasted those snippets into three different scripts, you will want a single wrapper. Here is a compact, dependency-free one — single file, no inheritance, no metaclass tricks — that handles the four things raw requests calls do not:
- Connection reuse — a single
Sessionkeeps TCP/TLS connections warm across calls, which matters when you are creating or inspecting many proxies in a loop. - Automatic auth headers — the Bearer token is set once on the session, not repeated in every call.
- Rate-limit awareness — a 429 response is turned into a typed
RateLimitedexception that carries theretry_aftervalue so your retry logic does not have to parse headers. - Transparent pagination —
iter_proxies()walks all pages so you can writefor p in client.iter_proxies():against accounts with large fleets without tracking page numbers yourself.
import os
import requests
class NodetonetClient:
def __init__(self, key=None, base='https://nodetonet.com/api/v1', timeout=10):
self.key = key or os.environ['NTN_KEY']
self.base = base.rstrip('/')
self.timeout = timeout
self.session = requests.Session()
self.session.headers.update({
'Authorization': f'Bearer {self.key}',
'User-Agent': 'nodetonet-python/0.1',
})
def _req(self, method, path, **kw):
r = self.session.request(
method, f'{self.base}{path}',
timeout=self.timeout, **kw,
)
if r.status_code == 429:
retry = int(r.headers.get('Retry-After', '1'))
raise RateLimited(retry)
r.raise_for_status()
return r.json()
# ---- proxies ----
def list_proxies(self, page=1, per_page=25):
return self._req('GET', '/proxies', params={'page': page, 'perPage': per_page})
def get_proxy(self, proxy_id):
return self._req('GET', f'/proxies/{proxy_id}')['data']
def create_proxy(self, **fields):
return self._req('POST', '/proxies', json=fields)['data']
def update_proxy(self, proxy_id, **fields):
return self._req('PATCH', f'/proxies/{proxy_id}', json=fields)['data']
def delete_proxy(self, proxy_id):
return self._req('DELETE', f'/proxies/{proxy_id}')
# ---- helpers ----
def iter_proxies(self):
page = 1
while True:
resp = self.list_proxies(page=page, per_page=100)
for p in resp['data']:
yield p
if page >= resp['meta']['totalPages']:
return
page += 1
class RateLimited(Exception):
def __init__(self, retry_after):
super().__init__(f'rate limited, retry after {retry_after}s')
self.retry_after = retry_after
5. Batch create and clean up
c = NodetonetClient()
# spin up ten proxies in a loop
for i in range(10):
p = c.create_proxy(
title=f'batch-{i}',
protocol='http',
port='auto',
tokenId='tok_3kzl8w9',
serverId='srv_de_1',
)
print(p['id'], f"http://{p['username']}:{p['password']}@{p['host']}:{p['port']}")
# later, tear down anything from this run
for p in c.iter_proxies():
if p['title'].startswith('batch-'):
c.delete_proxy(p['id'])
Because Nodetonet uses prepaid credit with no monthly subscription, proxies you delete stop accruing any cost immediately. This makes ephemeral-fleet patterns — create for a job, destroy when done — both practical and economical. You can read more about the billing model in pay-as-you-go pricing.
6. Sticky sessions via the API
When a downstream client needs a sticky session (for example, to hold a login state across multiple requests without changing IP), append a -session-XXXX suffix to the proxy username in the connection string you hand to that client. You do not need to create a separate proxy — the same endpoint serves both rotating and sticky traffic depending on the username suffix the client sends.
For an automated workflow where you generate per-session usernames programmatically:
import uuid
def sticky_url(proxy):
session_id = uuid.uuid4().hex[:8]
user = f"{proxy['username']}-session-{session_id}"
return f"http://{user}:{proxy['password']}@{proxy['host']}:{proxy['port']}"
p = c.create_proxy(title='checkout-flow', protocol='http',
port='auto', tokenId='tok_3kzl8w9', serverId='srv_de_1')
print(sticky_url(p))
7. API responses at a glance
The table below summarises the endpoints and HTTP methods you will use most, with their expected success status and what the data field contains:
| Operation | Method | Path | Success status | data field |
|---|---|---|---|---|
| List proxies | GET | /proxies | 200 | Array of proxy objects |
| Get one proxy | GET | /proxies/:id | 200 | Single proxy object |
| Create proxy | POST | /proxies | 201 | New proxy with credentials |
| Update proxy | PATCH | /proxies/:id | 200 | Updated proxy object |
| Delete proxy | DELETE | /proxies/:id | 204 | Empty |
8. Error handling worth doing
The three failure modes you will actually hit in production:
- 401 Unauthorized — the key was rotated or revoked. Re-read it from your secret store and retry once. If it still fails, escalate to a human.
- 429 Too Many Requests — back off for
retry_afterseconds and retry. The class above raisesRateLimitedwith that field already parsed out of the response header. - 5xx Server Error — transient platform issue. Retry a GET immediately; for POST, PATCH or DELETE wait a few seconds and only retry if you have made the operation idempotent on your side, for example by carrying your own
titleas a deduplication key and checking whether a proxy with that title already exists before creating again.
Prefer async Python? The same class shape works withhttpx.AsyncClient— swaprequests.Sessionfor an async client and addasync/awaitto each method. The wire protocol is identical; only the I/O layer changes.
What to read next
- Your first REST API call — the curl-level introduction this guide builds on.
- Bulk operations: managing 100 proxies — patterns for large fleet automation.
- Token groups and device pools — how to round-robin across many phones from a single endpoint.
- When to use rotating mobile proxies — decide between rotating and sticky before writing your client logic.
- Mobile proxies overview — platform capabilities behind the API calls you are making.
- Proxy checker — verify any proxy you create is live and returning the expected IP before you deploy it.
Ready to build? Create a free account and get your API key in under a minute.