netriskscan 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. netriskscan-0.1.0/.github/workflows/ci.yml +29 -0
  2. netriskscan-0.1.0/.github/workflows/publish.yml +46 -0
  3. netriskscan-0.1.0/.gitignore +15 -0
  4. netriskscan-0.1.0/CHANGELOG.md +14 -0
  5. netriskscan-0.1.0/CONTRIBUTING.md +49 -0
  6. netriskscan-0.1.0/LICENSE +21 -0
  7. netriskscan-0.1.0/PKG-INFO +349 -0
  8. netriskscan-0.1.0/README.md +308 -0
  9. netriskscan-0.1.0/SECURITY.md +31 -0
  10. netriskscan-0.1.0/examples/anonymous.py +22 -0
  11. netriskscan-0.1.0/examples/async_quickstart.py +16 -0
  12. netriskscan-0.1.0/examples/error_handling.py +44 -0
  13. netriskscan-0.1.0/examples/quickstart.py +17 -0
  14. netriskscan-0.1.0/examples/usage_quota.py +21 -0
  15. netriskscan-0.1.0/pyproject.toml +90 -0
  16. netriskscan-0.1.0/src/netriskscan/__init__.py +76 -0
  17. netriskscan-0.1.0/src/netriskscan/_internal.py +253 -0
  18. netriskscan-0.1.0/src/netriskscan/_meta_registry.py +34 -0
  19. netriskscan-0.1.0/src/netriskscan/async_client.py +151 -0
  20. netriskscan-0.1.0/src/netriskscan/client.py +147 -0
  21. netriskscan-0.1.0/src/netriskscan/exceptions.py +163 -0
  22. netriskscan-0.1.0/src/netriskscan/models/__init__.py +30 -0
  23. netriskscan-0.1.0/src/netriskscan/models/meta.py +42 -0
  24. netriskscan-0.1.0/src/netriskscan/models/risk.py +234 -0
  25. netriskscan-0.1.0/src/netriskscan/models/usage.py +57 -0
  26. netriskscan-0.1.0/src/netriskscan/py.typed +0 -0
  27. netriskscan-0.1.0/src/netriskscan/version.py +1 -0
  28. netriskscan-0.1.0/tests/__init__.py +0 -0
  29. netriskscan-0.1.0/tests/conftest.py +100 -0
  30. netriskscan-0.1.0/tests/test_async_client.py +78 -0
  31. netriskscan-0.1.0/tests/test_client.py +189 -0
  32. netriskscan-0.1.0/tests/test_errors.py +239 -0
  33. netriskscan-0.1.0/tests/test_internal.py +107 -0
  34. netriskscan-0.1.0/tests/test_models.py +92 -0
  35. netriskscan-0.1.0/tests/test_retry.py +116 -0
@@ -0,0 +1,29 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ test:
13
+ runs-on: ubuntu-latest
14
+ strategy:
15
+ matrix:
16
+ python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
17
+ steps:
18
+ - uses: actions/checkout@v7
19
+ - uses: actions/setup-python@v7
20
+ with:
21
+ python-version: ${{ matrix.python-version }}
22
+ cache: pip
23
+ - run: python -m pip install --upgrade pip
24
+ - run: pip install -e ".[dev]"
25
+ - run: ruff check .
26
+ - run: ruff format --check .
27
+ - run: mypy src
28
+ - run: pytest
29
+ - run: python -m build
@@ -0,0 +1,46 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*.*.*"
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ build:
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - uses: actions/checkout@v7
16
+ - uses: actions/setup-python@v7
17
+ with:
18
+ python-version: "3.12"
19
+ - run: python -m pip install --upgrade pip build
20
+ - run: pip install -e ".[dev]"
21
+ - run: ruff check .
22
+ - run: mypy src
23
+ - run: pytest
24
+ - run: python -m build
25
+ - uses: actions/upload-artifact@v4
26
+ with:
27
+ name: dist
28
+ path: dist/
29
+
30
+ # Uses PyPI Trusted Publishing (OIDC) -- no long-lived API token is stored
31
+ # as a repository secret. Before the first release, a maintainer must
32
+ # register this repo/workflow/environment as a "pending trusted publisher"
33
+ # for the `netriskscan` project at https://pypi.org/manage/account/publishing/
34
+ # (the project does not exist on PyPI yet, so this is a one-time manual step).
35
+ publish:
36
+ needs: build
37
+ runs-on: ubuntu-latest
38
+ environment: pypi
39
+ permissions:
40
+ id-token: write
41
+ steps:
42
+ - uses: actions/download-artifact@v4
43
+ with:
44
+ name: dist
45
+ path: dist/
46
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,15 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ .eggs/
5
+ build/
6
+ dist/
7
+ .venv/
8
+ venv/
9
+ .mypy_cache/
10
+ .pytest_cache/
11
+ .ruff_cache/
12
+ .coverage
13
+ htmlcov/
14
+ .vscode/
15
+ .idea/
@@ -0,0 +1,14 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file.
4
+
5
+ ## [0.1.0] - 2026-09-16
6
+
7
+ Initial release.
8
+
9
+ - `NetRiskScan` (sync) and `AsyncNetRiskScan` (async) clients for `GET /v1/ip-risk/{ip}` and `GET /v1/usage`.
10
+ - Anonymous access (no API key) and API key authentication via `NETRISKSCAN_API_KEY` or an explicit argument.
11
+ - Typed, immutable dataclass models preserving tri-state (`True`/`False`/`None`) detection flags.
12
+ - Full exception hierarchy mapped to the documented `/v1` error codes.
13
+ - Automatic retry with backoff and `Retry-After` support for `429`/`502`/`503`/`504`.
14
+ - Rate limit and quota metadata via `get_response_meta()`.
@@ -0,0 +1,49 @@
1
+ # Contributing
2
+
3
+ ## Setup
4
+
5
+ ```bash
6
+ python -m venv .venv
7
+ source .venv/bin/activate # .venv\Scripts\activate on Windows
8
+ pip install -e ".[dev]"
9
+ ```
10
+
11
+ ## Workflow
12
+
13
+ ```bash
14
+ ruff check . # lint
15
+ ruff format --check . # formatting
16
+ mypy src # type check
17
+ pytest # tests
18
+ python -m build # build the sdist/wheel
19
+ ```
20
+
21
+ All four must pass before a pull request is merged; CI runs the same checks.
22
+
23
+ ## Ground rules
24
+
25
+ - This SDK is a typed API client, not a risk-scoring engine. Do not add client-side logic that
26
+ recomputes, derives, or overrides `risk.index`, `risk.band`, or any detection flag -- those values
27
+ come from the server only.
28
+ - Preserve tri-state semantics (`True` / `False` / `None`) on every detection flag. Never coerce
29
+ `None` to `False`, and never coerce a missing/`null` `risk.index` to `0`.
30
+ - Unknown fields returned by the API must be ignored, not raise -- this keeps older SDK versions
31
+ working against a server that has added new fields.
32
+ - Do not read `process.env`-style configuration beyond what's documented (`NETRISKSCAN_API_KEY`).
33
+
34
+ ## Releasing
35
+
36
+ Version is set in `pyproject.toml` (`[project].version`) and mirrored in `src/netriskscan/version.py`.
37
+ To release: bump both, update `CHANGELOG.md`, commit, then push a `vX.Y.Z` tag -- `.github/workflows/publish.yml`
38
+ builds and publishes to PyPI via Trusted Publishing (OIDC), no stored token required. Before the very
39
+ first release, a maintainer must register this repository as a pending trusted publisher for the
40
+ `netriskscan` project at https://pypi.org/manage/account/publishing/ (one-time manual step, since the
41
+ project does not exist on PyPI yet).
42
+
43
+ ## Reporting bugs / requesting features
44
+
45
+ Open a [GitHub issue](https://github.com/TeamQQ/netriskscan-sdk-python/issues).
46
+
47
+ ## Security issues
48
+
49
+ See [SECURITY.md](SECURITY.md) -- do not open a public issue.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 NetRiskScan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,349 @@
1
+ Metadata-Version: 2.5
2
+ Name: netriskscan
3
+ Version: 0.1.0
4
+ Summary: Official Python SDK for IP risk, reputation, proxy, VPN, Tor and network intelligence using the NetRiskScan API.
5
+ Project-URL: Homepage, https://www.netriskscan.com/
6
+ Project-URL: Documentation, https://github.com/TeamQQ/netriskscan-sdk-python#readme
7
+ Project-URL: Developer API, https://www.netriskscan.com/
8
+ Project-URL: Repository, https://github.com/TeamQQ/netriskscan-sdk-python
9
+ Project-URL: Issues, https://github.com/TeamQQ/netriskscan-sdk-python/issues
10
+ Project-URL: Changelog, https://github.com/TeamQQ/netriskscan-sdk-python/blob/main/CHANGELOG.md
11
+ Project-URL: JavaScript SDK, https://www.npmjs.com/package/@netriskscan/sdk
12
+ Project-URL: CLI, https://www.npmjs.com/package/netriskscan-cli
13
+ Author: NetRiskScan
14
+ License-Expression: MIT
15
+ License-File: LICENSE
16
+ Keywords: abuse-detection,anonymous-ip,api-client,cybersecurity,datacenter-detection,fraud-detection,ip-intelligence,ip-lookup,ip-reputation,ip-risk,ip-security,netriskscan,network-intelligence,network-security,proxy-detection,risk-scoring,threat-intelligence,tor-detection,vpn-detection
17
+ Classifier: Development Status :: 4 - Beta
18
+ Classifier: Intended Audience :: Developers
19
+ Classifier: License :: OSI Approved :: MIT License
20
+ Classifier: Operating System :: OS Independent
21
+ Classifier: Programming Language :: Python :: 3
22
+ Classifier: Programming Language :: Python :: 3 :: Only
23
+ Classifier: Programming Language :: Python :: 3.9
24
+ Classifier: Programming Language :: Python :: 3.10
25
+ Classifier: Programming Language :: Python :: 3.11
26
+ Classifier: Programming Language :: Python :: 3.12
27
+ Classifier: Programming Language :: Python :: 3.13
28
+ Classifier: Topic :: Internet
29
+ Classifier: Topic :: Security
30
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
31
+ Classifier: Typing :: Typed
32
+ Requires-Python: >=3.9
33
+ Requires-Dist: httpx<1,>=0.24
34
+ Provides-Extra: dev
35
+ Requires-Dist: build>=1.2; extra == 'dev'
36
+ Requires-Dist: mypy>=1.10; extra == 'dev'
37
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
38
+ Requires-Dist: pytest>=8; extra == 'dev'
39
+ Requires-Dist: ruff>=0.6; extra == 'dev'
40
+ Description-Content-Type: text/markdown
41
+
42
+ # NetRiskScan Python SDK
43
+
44
+ Official Python SDK for the [NetRiskScan](https://www.netriskscan.com/) IP Risk & Network Intelligence API: IP reputation, proxy/VPN/Tor detection, datacenter and search-crawler identification, and network intelligence.
45
+
46
+ ```bash
47
+ pip install netriskscan
48
+ ```
49
+
50
+ ```python
51
+ from netriskscan import NetRiskScan
52
+
53
+ client = NetRiskScan() # no signup, no API key required
54
+
55
+ result = client.ip_risk("8.8.8.8")
56
+
57
+ print(result.risk.index) # 0-100, higher = cleaner. Example output; live scores can change.
58
+ print(result.risk.band) # "excellent" | "good" | "fair" | "poor" | "high_risk" | "unknown"
59
+ ```
60
+
61
+ ## Table of contents
62
+
63
+ - [Installation](#installation)
64
+ - [Quick start](#quick-start)
65
+ - [Anonymous usage](#anonymous-usage)
66
+ - [Authentication](#authentication)
67
+ - [IP risk lookup](#ip-risk-lookup)
68
+ - [Usage / quota](#usage--quota)
69
+ - [Async client](#async-client)
70
+ - [Error handling](#error-handling)
71
+ - [Rate limits](#rate-limits)
72
+ - [Configuration](#configuration)
73
+ - [Type hints](#type-hints)
74
+ - [Use cases](#use-cases)
75
+ - [API documentation](#api-documentation)
76
+ - [NetRiskScan ecosystem](#netriskscan-ecosystem)
77
+ - [Examples](#examples)
78
+ - [Security](#security)
79
+ - [License](#license)
80
+
81
+ ## Installation
82
+
83
+ Requires Python 3.9+.
84
+
85
+ ```bash
86
+ pip install netriskscan
87
+ ```
88
+
89
+ ## Quick start
90
+
91
+ ```python
92
+ from netriskscan import NetRiskScan
93
+
94
+ client = NetRiskScan()
95
+ result = client.ip_risk("8.8.8.8")
96
+
97
+ print(result.risk.index, result.risk.band)
98
+ print(result.network.organization, result.network.asn)
99
+ print(result.flags.proxy, result.flags.vpn, result.flags.tor)
100
+ ```
101
+
102
+ ## Anonymous usage
103
+
104
+ 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):
105
+
106
+ ```python
107
+ from netriskscan import NetRiskScan
108
+
109
+ client = NetRiskScan()
110
+ result = client.ip_risk("8.8.8.8")
111
+
112
+ if result.usage is not None: # only present on anonymous calls
113
+ print(result.usage.remaining, "of", result.usage.daily_limit, "requests left today")
114
+ ```
115
+
116
+ Anonymous quota, rate limits, and eligibility are decided entirely by the server -- the SDK never re-implements or assumes those business rules.
117
+
118
+ ## Authentication
119
+
120
+ Pass an API key explicitly, or set the `NETRISKSCAN_API_KEY` environment variable. Precedence: explicit argument > environment variable > anonymous.
121
+
122
+ ```python
123
+ from netriskscan import NetRiskScan
124
+
125
+ client = NetRiskScan(api_key="nrs_live_xxxxxxxxxxxxxxxxxxxx")
126
+ ```
127
+
128
+ ```bash
129
+ export NETRISKSCAN_API_KEY="nrs_live_xxxxxxxxxxxxxxxxxxxx"
130
+ ```
131
+
132
+ ```python
133
+ from netriskscan import NetRiskScan
134
+
135
+ client = NetRiskScan() # reads NETRISKSCAN_API_KEY if set, else anonymous
136
+ ```
137
+
138
+ The key is always sent as `Authorization: Bearer <api-key>` -- never as a URL query parameter, never logged, and never included in exception messages.
139
+
140
+ ## IP risk lookup
141
+
142
+ ```python
143
+ result = client.ip_risk("8.8.8.8")
144
+
145
+ result.risk.index # int | None -- 0-100 cleanliness score (higher = cleaner), None if unscoreable
146
+ result.risk.band # str | None -- "excellent" | "good" | "fair" | "poor" | "high_risk" | "unknown"
147
+ result.risk.assessment_grade # str -- "complete" | "partial" | "limited" | "insufficient"
148
+ result.risk.reasons # list[RiskReason] -- may be empty, never a signal by itself
149
+
150
+ result.network.type # "residential" | "mobile" | "hosting" | "datacenter" | "public_infrastructure" | ...
151
+ result.network.connection_type # "direct" | "vpn" | "proxy" | "tor" | ...
152
+ result.network.asn # e.g. "AS15169"
153
+ result.network.organization # e.g. "Google LLC"
154
+
155
+ result.flags.proxy # bool | None -- see "null is not false" below
156
+ result.flags.proxy_type # populated only when flags.proxy is True
157
+ result.flags.vpn # bool | None
158
+ result.flags.tor # bool | None -- Tor *exit* node specifically
159
+ result.flags.datacenter # bool | None
160
+ result.flags.scanner # bool | None -- behavioral scanner/bot activity
161
+ result.flags.abuse # bool | None
162
+ result.flags.search_crawler # bool | None -- verified search-engine crawler identity
163
+ result.flags.search_crawler_name # e.g. "Google", populated only when search_crawler is True
164
+
165
+ result.location # IpLocation | None -- network-level geolocation, not device GPS
166
+ result.tor # TorInfo | None -- present only when the address is a Tor relay
167
+ ```
168
+
169
+ ### The index is a cleanliness score, not a threat score
170
+
171
+ `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.
172
+
173
+ ### `None` is not `False`
174
+
175
+ Every detection flag (`proxy`, `vpn`, `tor`, `datacenter`, `scanner`, `abuse`, `search_crawler`) is a three-valued signal:
176
+
177
+ - `True` -- detected
178
+ - `False` -- checked, and confirmed not detected
179
+ - `None` -- unknown / not evaluated this round
180
+
181
+ 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.
182
+
183
+ ### An unscoreable address is a success, not an error
184
+
185
+ 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.
186
+
187
+ ### Search crawler identity vs. scanner behavior
188
+
189
+ `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.
190
+
191
+ ## Usage / quota
192
+
193
+ Requires an API key (there is no anonymous account to report usage for):
194
+
195
+ ```python
196
+ usage = client.usage()
197
+
198
+ print(usage.plan)
199
+ print(usage.units.used, "/", usage.units.limit)
200
+ print(usage.rate_limit.requests_per_minute)
201
+ ```
202
+
203
+ Calling `usage()` without an API key raises `ValidationError` immediately, without a network call.
204
+
205
+ ## Async client
206
+
207
+ Identical API, `httpx`-based async transport:
208
+
209
+ ```python
210
+ from netriskscan import AsyncNetRiskScan
211
+
212
+
213
+ async def main():
214
+ async with AsyncNetRiskScan() as client:
215
+ result = await client.ip_risk("8.8.8.8")
216
+ print(result.risk.index)
217
+ ```
218
+
219
+ ## Error handling
220
+
221
+ ```python
222
+ from netriskscan import (
223
+ NetRiskScan,
224
+ ValidationError,
225
+ AuthenticationError,
226
+ RateLimitError,
227
+ QuotaExceededError,
228
+ NotFoundError,
229
+ FeatureNotAvailableError,
230
+ ApiError,
231
+ TimeoutError,
232
+ NetworkError,
233
+ )
234
+
235
+ client = NetRiskScan(api_key="nrs_live_xxxxxxxxxxxxxxxxxxxx")
236
+
237
+ try:
238
+ result = client.ip_risk("8.8.8.8")
239
+ except ValidationError as e:
240
+ ... # bad IP address / bad request (HTTP 400)
241
+ except AuthenticationError as e:
242
+ ... # missing, invalid, or disabled API key (HTTP 401/403)
243
+ except QuotaExceededError as e:
244
+ ... # billing-period quota or anonymous daily limit exhausted (HTTP 429)
245
+ except RateLimitError as e:
246
+ ... # short-lived per-minute rate limit (HTTP 429); e.retry_after in seconds
247
+ except (NotFoundError, FeatureNotAvailableError):
248
+ ... # unknown route, or a documented capability not open yet (HTTP 404)
249
+ except ApiError as e:
250
+ ... # any other non-2xx response, e.g. HTTP 503 temporarily_unavailable
251
+ except TimeoutError as e:
252
+ ... # request did not complete in time; never retried automatically
253
+ except NetworkError as e:
254
+ ... # DNS/connection failure -- never reached the server
255
+ ```
256
+
257
+ 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.
258
+
259
+ ## Rate limits
260
+
261
+ ```python
262
+ from netriskscan import get_response_meta
263
+
264
+ result = client.ip_risk("8.8.8.8")
265
+ meta = get_response_meta(result)
266
+
267
+ if meta:
268
+ print(meta.rate_limit.remaining, "/", meta.rate_limit.limit)
269
+ print(meta.quota.remaining, "/", meta.quota.limit)
270
+ print(meta.request_id)
271
+ ```
272
+
273
+ `get_response_meta()` returns `None` for a header that was never sent -- it is never coerced to `0`.
274
+
275
+ ### Automatic retries
276
+
277
+ `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.
278
+
279
+ ## Configuration
280
+
281
+ ```python
282
+ from netriskscan import NetRiskScan
283
+
284
+ client = NetRiskScan(
285
+ api_key="nrs_live_xxxxxxxxxxxxxxxxxxxx", # optional; falls back to NETRISKSCAN_API_KEY, then anonymous
286
+ base_url="https://api.netriskscan.com", # override for testing/staging/mocking
287
+ timeout=10.0, # seconds, per attempt
288
+ max_retries=2,
289
+ max_retry_delay=10.0, # seconds
290
+ )
291
+ ```
292
+
293
+ Pass `http_client=httpx.Client(...)` (or `httpx.AsyncClient(...)` for `AsyncNetRiskScan`) to inject your own transport, for example `httpx.MockTransport` in tests.
294
+
295
+ ## Type hints
296
+
297
+ 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()`.
298
+
299
+ ## Use cases
300
+
301
+ - Detect proxy, VPN, and Tor infrastructure before signup or login
302
+ - Evaluate IP reputation as one signal in a fraud-prevention pipeline
303
+ - Distinguish verified search-engine crawlers from generic bot/scanner traffic
304
+ - Inspect datacenter and hosting traffic separately from residential networks
305
+ - Add network intelligence (ASN, organization, connection type) to abuse-prevention systems
306
+ - Gate CI/CD or infrastructure checks on a minimum risk index
307
+
308
+ ## API documentation
309
+
310
+ Base URL: `https://api.netriskscan.com`
311
+
312
+ - `GET /v1/ip-risk/{ip}` -- IP risk, reputation, and network intelligence (works with or without an API key)
313
+ - `GET /v1/usage` -- current billing-period usage and quota (requires an API key)
314
+
315
+ Full endpoint and error-code reference: [Developer API documentation](https://www.netriskscan.com/).
316
+
317
+ ## NetRiskScan ecosystem
318
+
319
+ - **Python SDK** (this package) -- `pip install netriskscan`
320
+ - **JavaScript / TypeScript SDK** -- [`@netriskscan/sdk`](https://www.npmjs.com/package/@netriskscan/sdk) on npm
321
+ - **CLI** -- [`netriskscan-cli`](https://www.npmjs.com/package/netriskscan-cli) (`npx netriskscan-cli check 8.8.8.8`)
322
+ - **Website / Developer API** -- [netriskscan.com](https://www.netriskscan.com/)
323
+ - **GitHub** -- [github.com/TeamQQ](https://github.com/TeamQQ)
324
+
325
+ Need JavaScript or TypeScript instead? See [`@netriskscan/sdk`](https://www.npmjs.com/package/@netriskscan/sdk).
326
+
327
+ ## Examples
328
+
329
+ Runnable scripts in [`examples/`](examples/):
330
+
331
+ | File | Demonstrates |
332
+ | --- | --- |
333
+ | `examples/quickstart.py` | Sync client, anonymous IP risk lookup |
334
+ | `examples/async_quickstart.py` | Async client |
335
+ | `examples/anonymous.py` | Reading the anonymous daily allowance from a response |
336
+ | `examples/usage_quota.py` | Authenticated usage/quota lookup |
337
+ | `examples/error_handling.py` | Catching the full exception hierarchy |
338
+
339
+ ## Security
340
+
341
+ - The API key is only ever sent as an `Authorization: Bearer` header, never in a URL, log line, or exception message.
342
+ - This SDK makes no calls to any host other than the configured `base_url`.
343
+ - No telemetry of any kind is collected or transmitted by this package.
344
+
345
+ Found a security issue? See [SECURITY.md](SECURITY.md).
346
+
347
+ ## License
348
+
349
+ MIT -- see [LICENSE](LICENSE).