NetRiskScan Python SDK
Official Python SDK for the NetRiskScan IP Risk & Network Intelligence API: IP reputation, proxy/VPN/Tor detection, datacenter and search-crawler identification, and network intelligence.
pip install netriskscan
from netriskscan import NetRiskScan
client = NetRiskScan() # no signup, no API key required
result = client.ip_risk("8.8.8.8")
print(result.risk.index) # 0-100, higher = cleaner. Example output; live scores can change.
print(result.risk.band) # "excellent" | "good" | "fair" | "poor" | "high_risk" | "unknown"
Table of contents
- Installation
- Quick start
- Anonymous usage
- Authentication
- IP risk lookup
- Usage / quota
- Async client
- Error handling
- Rate limits
- Configuration
- Type hints
- Use cases
- API documentation
- NetRiskScan ecosystem
- Examples
- Security
- License
Installation
Requires Python 3.9+.
pip install netriskscan
Quick start
from netriskscan import NetRiskScan
client = NetRiskScan()
result = client.ip_risk("8.8.8.8")
print(result.risk.index, result.risk.band)
print(result.network.organization, result.network.asn)
print(result.flags.proxy, result.flags.vpn, result.flags.tor)
Anonymous usage
The API can be called without an account, metered by the server (a daily allowance per source IP, currently 30 requests/day -- the SDK does not hardcode this limit; read it from the response):
from netriskscan import NetRiskScan
client = NetRiskScan()
result = client.ip_risk("8.8.8.8")
if result.usage is not None: # only present on anonymous calls
print(result.usage.remaining, "of", result.usage.daily_limit, "requests left today")
Anonymous quota, rate limits, and eligibility are decided entirely by the server -- the SDK never re-implements or assumes those business rules.
Authentication
Pass an API key explicitly, or set the NETRISKSCAN_API_KEY environment variable. Precedence: explicit argument > environment variable > anonymous.
from netriskscan import NetRiskScan
client = NetRiskScan(api_key="nrs_live_xxxxxxxxxxxxxxxxxxxx")
export NETRISKSCAN_API_KEY="nrs_live_xxxxxxxxxxxxxxxxxxxx"
from netriskscan import NetRiskScan
client = NetRiskScan() # reads NETRISKSCAN_API_KEY if set, else anonymous
The key is always sent as Authorization: Bearer <api-key> -- never as a URL query parameter, never logged, and never included in exception messages.
IP risk lookup
result = client.ip_risk("8.8.8.8")
result.risk.index # int | None -- 0-100 cleanliness score (higher = cleaner), None if unscoreable
result.risk.band # str | None -- "excellent" | "good" | "fair" | "poor" | "high_risk" | "unknown"
result.risk.assessment_grade # str -- "complete" | "partial" | "limited" | "insufficient"
result.risk.reasons # list[RiskReason] -- may be empty, never a signal by itself
result.network.type # "residential" | "mobile" | "hosting" | "datacenter" | "public_infrastructure" | ...
result.network.connection_type # "direct" | "vpn" | "proxy" | "tor" | ...
result.network.asn # e.g. "AS15169"
result.network.organization # e.g. "Google LLC"
result.flags.proxy # bool | None -- see "null is not false" below
result.flags.proxy_type # populated only when flags.proxy is True
result.flags.vpn # bool | None
result.flags.tor # bool | None -- Tor *exit* node specifically
result.flags.datacenter # bool | None
result.flags.scanner # bool | None -- behavioral scanner/bot activity
result.flags.abuse # bool | None
result.flags.search_crawler # bool | None -- verified search-engine crawler identity
result.flags.search_crawler_name # e.g. "Google", populated only when search_crawler is True
result.location # IpLocation | None -- network-level geolocation, not device GPS
result.tor # TorInfo | None -- present only when the address is a Tor relay
The index is a cleanliness score, not a threat score
risk.index runs 0-100 where higher means cleaner / more trustworthy. It is not a fraud or threat score where higher is worse. Never invert or rescale it client-side.
None is not False
Every detection flag (proxy, vpn, tor, datacenter, scanner, abuse, search_crawler) is a three-valued signal:
True-- detectedFalse-- checked, and confirmed not detectedNone-- unknown / not evaluated this round
Treating None as False turns "we don't know" into "we checked and it's clean," which is a different and stronger claim than the data supports. This SDK never performs that coercion, and code consuming these fields should not either.
An unscoreable address is a success, not an error
Private, loopback, and other special-purpose addresses return 200 OK with risk.index is None, risk.band is None, and risk.assessment_grade == "insufficient" -- not an exception. Check for None explicitly rather than assuming every successful call returns a numeric score.
Search crawler identity vs. scanner behavior
flags.search_crawler answers a narrow question: is this address in a range list the search-engine operator itself publishes? It is independent of flags.scanner, which tracks behavioral scanning/bot activity. A verified crawler is not automatically "not a scanner," and vice versa -- read both.
Usage / quota
Requires an API key (there is no anonymous account to report usage for):
usage = client.usage()
print(usage.plan)
print(usage.units.used, "/", usage.units.limit)
print(usage.rate_limit.requests_per_minute)
Calling usage() without an API key raises ValidationError immediately, without a network call.
Async client
Identical API, httpx-based async transport:
from netriskscan import AsyncNetRiskScan
async def main():
async with AsyncNetRiskScan() as client:
result = await client.ip_risk("8.8.8.8")
print(result.risk.index)
Error handling
from netriskscan import (
NetRiskScan,
ValidationError,
AuthenticationError,
RateLimitError,
QuotaExceededError,
NotFoundError,
FeatureNotAvailableError,
ApiError,
TimeoutError,
NetworkError,
)
client = NetRiskScan(api_key="nrs_live_xxxxxxxxxxxxxxxxxxxx")
try:
result = client.ip_risk("8.8.8.8")
except ValidationError as e:
... # bad IP address / bad request (HTTP 400)
except AuthenticationError as e:
... # missing, invalid, or disabled API key (HTTP 401/403)
except QuotaExceededError as e:
... # billing-period quota or anonymous daily limit exhausted (HTTP 429)
except RateLimitError as e:
... # short-lived per-minute rate limit (HTTP 429); e.retry_after in seconds
except (NotFoundError, FeatureNotAvailableError):
... # unknown route, or a documented capability not open yet (HTTP 404)
except ApiError as e:
... # any other non-2xx response, e.g. HTTP 503 temporarily_unavailable
except TimeoutError as e:
... # request did not complete in time; never retried automatically
except NetworkError as e:
... # DNS/connection failure -- never reached the server
Every exception carries .message, .status_code, .code (the server's open-vocabulary error code), and .request_id where available -- include request_id when reporting issues. QuotaExceededError is a subclass of RateLimitError, so except RateLimitError alone catches both.
Rate limits
from netriskscan import get_response_meta
result = client.ip_risk("8.8.8.8")
meta = get_response_meta(result)
if meta:
print(meta.rate_limit.remaining, "/", meta.rate_limit.limit)
print(meta.quota.remaining, "/", meta.quota.limit)
print(meta.request_id)
get_response_meta() returns None for a header that was never sent -- it is never coerced to 0.
Automatic retries
GET requests are retried automatically for HTTP 429/502/503/504 and for transient network failures, honoring the server's Retry-After header when present, otherwise using exponential backoff with jitter. Retries are capped by max_retries (default 2) and max_retry_delay (default 10 seconds); a Retry-After longer than max_retry_delay raises immediately instead of blocking. 400/401/403/404 responses and request timeouts are never retried.
Configuration
from netriskscan import NetRiskScan
client = NetRiskScan(
api_key="nrs_live_xxxxxxxxxxxxxxxxxxxx", # optional; falls back to NETRISKSCAN_API_KEY, then anonymous
base_url="https://api.netriskscan.com", # override for testing/staging/mocking
timeout=10.0, # seconds, per attempt
max_retries=2,
max_retry_delay=10.0, # seconds
)
Pass http_client=httpx.Client(...) (or httpx.AsyncClient(...) for AsyncNetRiskScan) to inject your own transport, for example httpx.MockTransport in tests.
Type hints
The package ships py.typed and is fully annotated. Results are plain, immutable dataclasses -- no ORM-style magic, easy to log, cache, or serialize with dataclasses.asdict().
Use cases
- Detect proxy, VPN, and Tor infrastructure before signup or login
- Evaluate IP reputation as one signal in a fraud-prevention pipeline
- Distinguish verified search-engine crawlers from generic bot/scanner traffic
- Inspect datacenter and hosting traffic separately from residential networks
- Add network intelligence (ASN, organization, connection type) to abuse-prevention systems
- Gate CI/CD or infrastructure checks on a minimum risk index
API documentation
Base URL: https://api.netriskscan.com
GET /v1/ip-risk/{ip}-- IP risk, reputation, and network intelligence (works with or without an API key)GET /v1/usage-- current billing-period usage and quota (requires an API key)
Full endpoint and error-code reference: Developer API documentation.
NetRiskScan ecosystem
- Python SDK (this package) --
pip install netriskscan - JavaScript / TypeScript SDK --
@netriskscan/sdkon npm - CLI --
netriskscan-cli(npx netriskscan-cli check 8.8.8.8) - Website / Developer API -- netriskscan.com
- GitHub -- github.com/TeamQQ
Need JavaScript or TypeScript instead? See @netriskscan/sdk.
Examples
Runnable scripts in examples/:
| File | Demonstrates |
|---|---|
examples/quickstart.py |
Sync client, anonymous IP risk lookup |
examples/async_quickstart.py |
Async client |
examples/anonymous.py |
Reading the anonymous daily allowance from a response |
examples/usage_quota.py |
Authenticated usage/quota lookup |
examples/error_handling.py |
Catching the full exception hierarchy |
Security
- The API key is only ever sent as an
Authorization: Bearerheader, never in a URL, log line, or exception message. - This SDK makes no calls to any host other than the configured
base_url. - No telemetry of any kind is collected or transmitted by this package.
Found a security issue? See SECURITY.md.
License
MIT -- see LICENSE.
Release files for netriskscan 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| netriskscan-0.1.0.tar.gz | 24.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| netriskscan-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 46.9 kB
Release files / netriskscan-0.1.0.tar.gz
| Download URL | netriskscan-0.1.0.tar.gz |
|---|---|
| Size | 24.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
bf2494a519409a37e20b961412ffc81d02754c3bd1d5f543d693725f769a70fc
|
|
BLAKE2b-256 checksum How to use checksums |
778ad94b34edfba2f686ecad80d120d24934b7a5820d4fe35b59d5fe3a31f14a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 16, 2026.
Transparency logRelease files / netriskscan-0.1.0-py3-none-any.whl
| Download URL | netriskscan-0.1.0-py3-none-any.whl |
|---|---|
| Size | 22.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b95c78d41cc971d69cedbde1353b19ece39645002a4a1cf1d0004b40a78dc109
|
|
BLAKE2b-256 checksum How to use checksums |
9aa9fbc9861644d8617b5a9801190d0b71c6792127864dfb76dcdc219b4f70dc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 16, 2026.
Transparency log