SDK reference
Client libraries
Official clients for Python and Node.js / TypeScript. Both are thin wrappers over the same REST API — they add typed responses, error classes and quota parsing, but nothing you cannot do with a plain HTTPS request.
Using another language? The API is a single JSON POST. See the API reference — there is nothing an SDK unlocks that raw HTTP does not.
Install
# Python (3.8+) pip install predax
The Node.js client is not on npm yet. It is built and tested, and everything documented below is accurate, but npm install predax will not resolve until it is published. Until then, Node users should call the API directly — it is a single JSON POST, described in the API reference.
Both clients default to https://predax.io. There is no api. subdomain — the API is served from the main domain under /api/v1.
Your first check
Authenticate with an API key from your dashboard. Keys are prefixed prdx_live_ (or prdx_test_). Treat a key like a password: it belongs on your server, never in browser or mobile code, where anyone can read it and spend your quota.
# Python
from predax import Client
client = Client("prdx_live_...")
res = client.check_ip("8.8.8.8")
print(res.risk_score) # 0-100
print(res.is_proxy) # bool
print(res.country) # "US"// Node.js / TypeScript
import { PredaxClient } from "predax";
const client = new PredaxClient({ apiKey: "prdx_live_..." });
const res = await client.checkIp("8.8.8.8");
console.log(res.classification.risk_score); // 0-100
console.log(res.classification.is_proxy); // boolean
console.log(res.location?.country_code); // "US"The two clients return different shapes
This trips people up, so it is worth stating plainly:
- Python flattens the response.
check_ip()returns a frozen dataclass with the fields you usually want promoted to the top level —risk_score,is_vpn,is_proxy,is_tor,is_datacenter,is_web_crawler,country,asn,asn_name. The untouched API payload is always available onres.rawif you need a field the dataclass does not expose. - Node returns the API shape as-is. Classification flags live under
res.classification, network details underres.network, geolocation underres.location.
One consequence worth knowing: the API calls the verified-crawler flag is_crawler, and the Python client exposes it as is_web_crawler. In Node it stays res.classification.is_crawler.
Handling errors
Both clients raise typed errors rather than returning status codes. The two you must handle in production are quota and rate limiting — everything else is a bug in your integration or an outage.
# Python
from predax import Client, QuotaExceededException, RateLimitException, APIException
try:
res = client.check_ip(ip)
except QuotaExceededException as e:
# 402 - allowance spent. e.quota carries the limits and reset time.
# Fail OPEN here: do not block real visitors because billing ran out.
allow_request()
except RateLimitException as e:
# 429 - too fast. e.retry_after tells you how many seconds to wait.
time.sleep(e.retry_after or 1)
except APIException as e:
# Anything else: e.status_code, e.headers, e.body
log.warning("predax check failed: %s", e)
allow_request()// Node.js / TypeScript
import { PredaxClient, QuotaExceededError, RateLimitError, APIError } from "predax";
try {
const res = await client.checkIp(ip);
} catch (err) {
if (err instanceof QuotaExceededError) {
allowRequest(); // 402 - fail open
} else if (err instanceof RateLimitError) {
await sleep((err.retryAfterSeconds ?? 1) * 1000);
} else if (err instanceof APIError) {
allowRequest(); // treat an outage as "allow"
}
}Fail open, not closed. If Predax is unreachable or your allowance is spent, let the request through. A screening service that goes down should cost you nothing; one that blocks every visitor when it goes down takes your site down with it.
Knowing where your quota went
Every response carries quota headers, and both clients parse them for you. The Python client exposes get_quota(); on a 402 the same information is attached to the exception.
# Python q = client.get_quota() print(q.daily_remaining, q.daily_limit, q.daily_reset_at) # Node const usage = await client.getUsage(); console.log(usage.daily?.remaining, usage.quota);
The daily allowance resets at 00:00 UTC, not on the first of the month. If you are burning through it faster than expected, the usual cause is a missing cache in your own code — check request logs, which breaks usage down per visitor IP and shows repeat lookups first.
Bulk checks
Use bulk when you already have a list — log analysis, backfilling historical orders, scoring a signup table. It is one HTTP round trip instead of N, which matters far more than the per-IP cost. For live traffic you are screening one visitor at a time anyway, so the single check is the right call.
# Python res = client.check_bulk(["8.8.8.8", "1.1.1.1"]) // Node const res = await client.checkBulk(["8.8.8.8", "1.1.1.1"]);
Each address in the batch counts against your allowance individually. For lists larger than the synchronous limit, use bulk jobs, which process asynchronously and let you download results when finished.
Async (Python)
AsyncClient mirrors Client method-for-method. Use it if your app already runs on asyncio — a blocking HTTP call inside an async request handler stalls the whole event loop.
from predax import AsyncClient
client = AsyncClient("prdx_live_...")
res = await client.check_ip("8.8.8.8")
await client.aclose()Local development
Point the client at your own instance by passing a base URL explicitly.
# Python
client = Client("prdx_test_...", base_url="http://localhost:8000")
// Node
new PredaxClient({ apiKey: "prdx_test_...", baseUrl: "http://localhost:8000" })If your client runs inside Docker (WordPress/WooCommerce, for example), localhost resolves to the container, not your machine — use host.docker.internal or your host IP instead.