Repository navigation
v0.1.3
The library opens sockets now. Two calls reach the network and neither runs on
import: proxy.check() sends one CONNECT to the gateway, and Client() talks
to the dashboard account API. Everything from 0.1.2 still opens nothing -
Proxy builds a string and hands it to whatever client you already have.
proxy.check()
One CONNECT, the reply handed back whole: status, the reason phrase verbatim,
every header, elapsed seconds, the exit address when the gateway sends one, and
the gateway's own explanation of that status.
A refusal is a return value, not an exception. A 407 is the gateway
answering the question, so it comes back as a Check. CheckError is raised
only when nothing came back at all.
The CONNECT is hand-rolled rather than delegated, because of what an HTTP
library does to the diagnosis: requests reports a failed tunnel as
ProxyError('Unable to connect to proxy', OSError('Tunnel connection failed: 407 ...')), where the status survives as text inside a nested exception and the
reason phrase and every header are gone.
The account API
Client() wraps 19 of the vendor's 24 paths - the catalogues, statistics,
sub-users and the IP whitelist. The five that are not wrapped are listed in the
CHANGELOG rather than left silent.
It was written against the vendor's OpenAPI document and then measured against
the live server on 2026-09-09, and the document is wrong in four places that a
generated client would ship:
- Statistics dates are
dd-mm-yyyy, not ISO. The document writes the format
both ways -dd-mm-yyyyin the prose,format: datein the type - and the
prose is the half that is right.start=20-08-2026answers 200 with 21 data
points;start=2026-08-20answers 400 with a body byte-identical to the one
start=not-a-datedraws. So ISO is not a rival spelling the server declines,
it is a string the server cannot parse. - Three paging conventions, not one -
limit/offset,page/per_page
andpage/page_size- and an unknown query parameter is ignored, so a
hard-coded pair silently does nothing on two thirds of the surface. - Sub-user rows arrive under
payload, notresults, in an envelope with
nocountand nonext. whitelist/ip/upsertis refused 400 unlessprotocolis sent, though the
document marks it optional withdefault: "HTTP". The server does not apply
its own default, so this package sends it on every call.
iterate() stops on an empty page rather than a short one, because this server
caps cities at 1000 rows of 1965 and a short page is not the end.
Notes
RateLimitError.retry_after is always None from this client and the 429
message now says so. Transport returns (status, bytes) and the response
headers are discarded before anything could read Retry-After; widening that
return type is a public protocol change across four SDKs and is not made here.
The API key is a JWT that lives 1800 seconds, exp - iat. No path in the
vendor's 24 issues or refreshes one and the document does not mention an expiry,
so a Client held longer than half an hour begins raising AuthError for a
reason no response distinguishes from a wrong key. The message names the clock
as a third possibility. Nothing here reads exp or renews on its own.
This is the first release since the repository went public, so the Source and
Issues links reach the PyPI project page for the first time - metadata only
travels with a release, never with a visibility flip.
Full detail, with the run behind each claim, is in
CHANGELOG.md.