Skip to content

v0.1.3

Choose a tag to compare

@alkaz-nodemaven alkaz-nodemaven released this 09 Sep 17:41
· 11 commits to main since this release

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-yyyy in the prose, format: date in the type - and the
    prose is the half that is right. start=20-08-2026 answers 200 with 21 data
    points; start=2026-08-20 answers 400 with a body byte-identical to the one
    start=not-a-date draws. 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
    and page/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, not results, in an envelope with
    no count and no next.
  • whitelist/ip/upsert is refused 400 unless protocol is sent, though the
    document marks it optional with default: "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.