Skip to content

Latest commit

 

History

10 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

NodeMaven

nodemaven

Builds the proxy username a gateway expects, and refuses the input it would silently drop.

pypi python ci

license

Quickstart · Sticky sessions · Why the validation is the point · What it does not do · Other gateways · Docs

Build and validate proxy connection strings.

This library opens no socket. It builds the username a proxy gateway expects, refuses the input that gateway would mishandle, and hands the result to whatever HTTP client you already use.

pip install nodemaven

Quickstart

login and password are the Proxy Username and Proxy Password assigned under Proxy Setup in the dashboard - a separate pair from the account you sign in with. The other option there is IP whitelisting, which needs no credentials in the username at all; both are described in authentication methods.

import requests
from nodemaven import Proxy

proxy = Proxy(login="your-login", password="your-password",
              country="us", filter="medium")

r = requests.get("https://api.ipify.org", proxies=proxy.requests())
print(r.text)

No account? Any proxy you already have works. A gateway is a data description, not a code path, so one that ships no definition here goes through the same builder and the same validation:

import requests
from nodemaven import Proxy, Provider

# An empty known_params is not a stub. It says nobody has established what this
# gateway recognises, so every parameter is refused rather than sent to be
# silently dropped - see "Why the validation is the point" below.
mine = Provider(id="mine", label="My proxy", known_params=frozenset())

proxy = Proxy(provider=mine, login="your-login", password="your-password",
              host="proxy.example.com", port=8000)

r = requests.get("https://api.ipify.org", proxies=proxy.requests())
print(r.text)

Describe the parameters it does take and it validates those too - see Other gateways. Everything from here to the end of that section builds strings offline and opens no socket at all.

Credentials can come from the environment instead, so nothing is in your source:

# NODEMAVEN_LOGIN and NODEMAVEN_PASSWORD
proxy = Proxy(country="us", filter="medium")

The same identity, for other clients:

proxy.url()          # http://user:pass@gate.nodemaven.com:8080  - httpx, aiohttp, curl
proxy.requests()     # {"http": ..., "https": ...}
proxy.httpx()        # {"http://": ..., "https://": ...}
proxy.playwright()   # {"server": ..., "username": ..., "password": ...}
proxy.username       # the username on its own
proxy.server         # host:port, no credentials

With Playwright, Patchright or Puppeteer:

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch()
    context = browser.new_context(proxy=proxy.playwright())

Parameters

What the shipped NodeMaven definition accepts. Every name here was confirmed against the gateway rather than transcribed:

parameter what it selects values measured to work
country country code, or any us, de, ...
region area inside the country a name
city city inside the country a name
isp the exit's ISP a name
sid the sticky session - see below any string with no -
ttl how long that session is held 10m, 1m
filter IP quality low, medium, high (2026-08-13)
speed connection speed class fast, slow (2026-08-12)
ipv4 force IPv4 True / False

Names are validated. Values, on this gateway, are not. Passing a name that is not in this table raises before anything is sent, because the gateway answers an unknown name with 200 and drops the setting. Values are passed through, because what is known is which ones have been observed to work - and that is not the same as the set the gateway accepts. Refusing on a guessed list would block a setting that would have worked, which is the worse mistake of the two. The schema does carry a per-parameter list of legal values and refuses anything outside it; the shipped definition leaves that list empty for every parameter, deliberately, and a definition you write yourself gets the check as soon as you fill it in.

Credentials come from NODEMAVEN_LOGIN and NODEMAVEN_PASSWORD when not passed in, and the gateway address from NODEMAVEN_HOST and NODEMAVEN_PORT.

Errors

All four inherit from NodeMavenError, so one except catches everything this library raises.

exception raised when
ParamError a parameter name is unknown, a value is empty, a value contains the gateway's separator, or a value is outside a list the definition declares
CredentialsError no login, no password, or no gateway address, from arguments or environment
ProviderError a gateway definition is missing, unreadable, or internally inconsistent
NodeMavenError the base, never raised on its own

ParamError also covers the two structural cases: session() on a definition that declares no session parameter, and a definition whose parameter names collide with login, password, host, port or provider.

Nothing here is raised from a response, because nothing here sends one. Every failure this library reports is a failure it found before a socket existed.

Sticky sessions

One Proxy is one identity. Pin it to a sticky session:

held = proxy.session("order4417")

A session id cannot contain the character the gateway separates parameters with, which for this one is -, and passing one raises rather than connecting. That is measured and not a precaution: on 2026-08-20 a probe opened tunnels with sid-order8e3bf9-4417 and with sid-order8e3bf9, four rounds each, interleaved, and both landed on one exit address while a third arm spelled sid-order8e3bf94417 held a different one throughout. The gateway cuts the value at the separator and reads the rest as something else, so every order id beginning order would quietly share one session and one exit.

The session key is the whole parameter set, not the session id. country=us, sid=A and country=us, sid=A, filter=medium are two different sessions on the gateway, so adding or removing any parameter moves you to a different exit address. That is why parameters change through a method that returns a new object rather than by assignment - the move is a different identity, and the code should say so:

germany = proxy.replace(country="de")   # a new identity, a new exit
plain   = proxy.replace(filter=None)    # also a new identity

Why the validation is the point

A gateway is bad at telling you that you got the username wrong. Measured against this one on 2026-08-10, seven kinds of bad input produce seven different reactions and not one of them names the cause:

you sent the gateway answers
bad country 406 Not Acceptable
bad region 406 Not Acceptable
bad city 500 Internal Server Error
bad filter value 407 Proxy Authentication Required
bad ttl value 407 Proxy Authentication Required
empty value nothing, the connection hangs about 20 s
unknown parameter name 200, and the parameter is ignored

The two 407 replies send you to check credentials that are correct. The last row is worse than any of them: the request succeeds, your code carries on, and the setting you asked for was never applied. Nothing that comes back over the wire can tell you.

So this library checks before anything is sent:

>>> Proxy(login="u", password="p", contry="us")
ParamError: NodeMaven does not know the parameter 'contry': it is answered with
200 and dropped, so the connection would succeed and your setting would NOT be
applied. Known: ['city', 'country', 'filter', 'ipv4', 'isp', 'region', 'sid',
'speed', 'ttl']

What this library does not do

It does not retry. That is deliberate, and it is the one design decision here taken against a measurement rather than a preference.

Retrying a refused request is the thing that most reliably makes the next one worse: each retry confirms automation to the target and burns the exit range for everyone else sharing the pool. Measured over 1464 attempts, the chance that the next attempt succeeds, by how many failures came immediately before it:

failures before P(next attempt succeeds)
0 75%
1 21%
3 5.9%
5 5.8%
6 1.6%
7-9 0.5%

294 attempts were spent past six consecutive failures and returned 3 pages - 98 attempts per delivered page, against 1.7 in a healthy session. A library that shipped automatic retry as a default would be spending that on your behalf without telling you.

Those 1464 attempts, and the cells they came from, are in nodemaven/proxy-benchmark - the harness that measured them, open source, so the table above can be re-run rather than believed.

It also does not own an HTTP client, a connection pool or a browser. Those are yours, and they are better than anything a vendor SDK would bundle.

Other gateways

Parameters are data, not hardcoded keywords. A gateway is its prefix, separators, session parameter and the set of parameter names it actually recognises - and a definition written by you goes through the same builder and the same validation as the one shipped here. Either build it in place, as in the quickstart, or keep it in a TOML file:

# my-gateway.toml
label = "My proxy"
known_params = ["country", "session"]
session_param = "session"
host = "proxy.example.com"
port = 8000
from nodemaven import Proxy, load_file

mine = load_file("my-gateway.toml")
proxy = Proxy(provider=mine, login="u", password="p", country="us")
proxy.session("order4417")     # u-country-us-session-order4417

known_params is the whole point of the file: name a parameter that is not in it and the call raises instead of connecting. Leave the list empty and every parameter is refused, which is the correct thing to say about a gateway whose dialect nobody has established.

Credentials fall back to the environment under the definition's id in upper case, so this one reads MY_GATEWAY_LOGIN and MY_GATEWAY_PASSWORD and never NODEMAVEN_*. One process can hold several gateways without their credentials reaching each other.

The id comes from the filename, not from the variable you assign it to. load_file("my-gateway.toml") is my-gateway however it is named in your code, and - becomes _ in the variable names. Pass provider_id= to say it outright. The error raised when a credential is missing prints the exact pair it looked for, so this is one guess you never have to make.

Every definition carries a status. measured means traffic has gone through that gateway and the dialect was read off the wire. documented means it was transcribed from documentation and never exercised. Only nodemaven is shipped here, and it is measured.

Requirements

Python 3.9 or newer. No dependencies on 3.11 and newer; tomli on older ones.

Changes

CHANGELOG.md. Entries carry the probe and the date behind any change to what the gateway is believed to accept.

License

MIT.

About

Python SDK for the NodeMaven residential and mobile proxy gateway: builds the gateway username, validates it client-side, and refuses parameters the gateway would silently drop. Opens no socket, holds no session, retries nothing.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages