← Back to blog
$ curl -x http://user:pass@xxx.nodetonet.com:48888 https://api.ipify.org→ 188.114.96.7$ curl ... -H "X-Session: abc" # sticky→ 188.114.96.7 # same IP (TTL 600s)$ curl ... -H "X-Rotate: 1" # rotate→ 92.184.117.42 # new IP PYTHON SDK nodetonet.com

Programmatic tunnel creation in Python — a tiny Nodetonet client

N Nodetonet Team
April 3, 2026 8 min read

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:

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 proxiesGET/proxies200Array of proxy objects
Get one proxyGET/proxies/:id200Single proxy object
Create proxyPOST/proxies201New proxy with credentials
Update proxyPATCH/proxies/:id200Updated proxy object
Delete proxyDELETE/proxies/:id204Empty

8. Error handling worth doing

The three failure modes you will actually hit in production:

Prefer async Python? The same class shape works with httpx.AsyncClient — swap requests.Session for an async client and add async/await to each method. The wire protocol is identical; only the I/O layer changes.

What to read next

Ready to build? Create a free account and get your API key in under a minute.

Frequently asked questions

Do I need a special Python SDK to use the Nodetonet API?
No. The API is plain JSON over HTTPS with a Bearer token, so the standard requests library is all you need. This post shows a complete reusable client class built on requests with no third-party dependencies.
How do I create a mobile proxy with a specific country or carrier from Python?
Pass a usernameModifier field in the POST payload when creating the proxy, for example "-country-tr" for Turkey or a carrier tag. The response includes ready-to-use credentials. See the geo-targeting documentation for the full list of modifiers.
How do I handle rate limiting when making many API calls?
The API returns HTTP 429 with a Retry-After header when you exceed the rate limit. The NodetonetClient class in this post catches 429 and raises a typed RateLimited exception that carries the retry_after value in seconds, so your code can sleep and retry without parsing headers manually.
How do I create a sticky session proxy from the API?
You do not need to create a separate proxy for sticky sessions. Create one proxy normally via the API, then append -session-XXXX to the proxy username in the connection string you hand to each downstream client. That suffix pins the exit IP for the duration of the session. See the sticky sessions guide for details.
Can I use async Python (asyncio / httpx) instead of requests?
Yes. The NodetonetClient class in this post uses requests.Session, but the same structure works with httpx.AsyncClient. Swap the session object, add async/await to each method, and everything else stays the same — the REST API itself is stateless and protocol-agnostic.
Does deleting a proxy via the API stop billing immediately?
Yes. Nodetonet uses prepaid credit with no subscription. When you call DELETE on a proxy, it is torn down within seconds on every edge node and stops accruing any usage cost. This makes ephemeral fleet patterns — create for a job, destroy when done — both practical and cost-efficient.
N

Nodetonet Team

Building Nodetonet — a prepaid proxy + tunneling platform that replaces ngrok, Cloudflared and a residential proxy provider with a single panel.

Related posts