domaintools-api 2.9.0__tar.gz → 2.10.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 (49) hide show
  1. {domaintools_api-2.9.0/domaintools_api.egg-info → domaintools_api-2.10.0}/PKG-INFO +90 -5
  2. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/README.md +89 -4
  3. domaintools_api-2.10.0/VERSION +1 -0
  4. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/domaintools/_version.py +1 -1
  5. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/domaintools/api.py +305 -98
  6. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/domaintools/base_results.py +3 -0
  7. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/domaintools/cli/api.py +9 -4
  8. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/domaintools/cli/commands/feeds.py +438 -83
  9. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/domaintools/exceptions.py +4 -0
  10. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/domaintools/filters.py +6 -1
  11. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/domaintools/results.py +1 -0
  12. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/domaintools/utils.py +8 -6
  13. {domaintools_api-2.9.0 → domaintools_api-2.10.0/domaintools_api.egg-info}/PKG-INFO +90 -5
  14. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/tests/test_api.py +151 -18
  15. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/tests/test_filters.py +29 -1
  16. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/tests/test_utils.py +14 -6
  17. domaintools_api-2.9.0/VERSION +0 -1
  18. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/LICENSE +0 -0
  19. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/domaintools/__init__.py +0 -0
  20. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/domaintools/cli/__init__.py +0 -0
  21. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/domaintools/cli/commands/__init__.py +0 -0
  22. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/domaintools/cli/commands/accounts.py +0 -0
  23. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/domaintools/cli/commands/detects.py +0 -0
  24. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/domaintools/cli/commands/domains.py +0 -0
  25. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/domaintools/cli/commands/ips.py +0 -0
  26. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/domaintools/cli/commands/iris.py +0 -0
  27. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/domaintools/cli/constants.py +0 -0
  28. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/domaintools/cli/main.py +0 -0
  29. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/domaintools/cli/utils.py +0 -0
  30. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/domaintools/constants.py +0 -0
  31. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/domaintools/decorators.py +0 -0
  32. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/domaintools/docstring_patcher.py +0 -0
  33. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/domaintools/request_validator.py +0 -0
  34. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/domaintools/specs/iris-openapi.yaml +0 -0
  35. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/domaintools_api.egg-info/SOURCES.txt +0 -0
  36. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/domaintools_api.egg-info/dependency_links.txt +0 -0
  37. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/domaintools_api.egg-info/entry_points.txt +0 -0
  38. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/domaintools_api.egg-info/requires.txt +0 -0
  39. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/domaintools_api.egg-info/top_level.txt +0 -0
  40. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/domaintools_async/__init__.py +0 -0
  41. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/pyproject.toml +0 -0
  42. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/setup.cfg +0 -0
  43. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/setup.py +0 -0
  44. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/tests/test_async.py +0 -0
  45. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/tests/test_cli.py +0 -0
  46. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/tests/test_decorators.py +0 -0
  47. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/tests/test_docstring_patcher.py +0 -0
  48. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/tests/test_iris_enrich_account_info.py +0 -0
  49. {domaintools_api-2.9.0 → domaintools_api-2.10.0}/tests/test_request_validator.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: domaintools_api
3
- Version: 2.9.0
3
+ Version: 2.10.0
4
4
  Summary: DomainTools Official Python API
5
5
  Author-email: DomainTools <integrations@domaintools.com>
6
6
  License: The MIT License (MIT)
@@ -311,6 +311,19 @@ Please see the [supported versions](https://github.com/DomainTools/python_api/ra
311
311
  for the DomainTools Python support policy.
312
312
 
313
313
 
314
+ Authentication
315
+ ===================
316
+
317
+ The wrapper supports two authentication modes, selected automatically based on the product:
318
+
319
+ | Product | Default method | Params sent |
320
+ |---|---|---|
321
+ | Standard API (Iris, Whois, etc.) | HMAC-SHA256 signed | `api_username`, `timestamp`, `signature` as query params |
322
+ | Real-Time Threat Feeds (RTTF) | Header authentication | `X-Api-Key` header |
323
+
324
+ RTTF feeds also support HMAC signing as an opt-in via `always_sign_api_key=True` — see the RTTF section below.
325
+
326
+
314
327
  Real-Time Threat Feeds
315
328
  ===================
316
329
 
@@ -322,18 +335,24 @@ Custom parameters aside from the common `GET` Request parameters:
322
335
  api = API(USERNAME, KEY)
323
336
  api.nod(endpoint="feed", **kwargs)
324
337
  ```
325
- - `header_authentication`: by default, we're using API Header Authentication. Set this False if you want to use API Key and Secret Authentication. Apparently, you can't use API Header Authentication for `download` endpoints so this will be defaulted to `False` even without explicitly setting it.
338
+ - `header_authentication`: by default, all RTTF endpoints (both `feed` and `download`) use API Header Authentication, sending the API key via the `X-Api-Key` header. Set this to `False` to pass the API key as a query parameter instead.
326
339
  ```python
327
340
  api = API(USERNAME, KEY, header_authentication=False)
328
341
  api.nod(**kwargs)
329
342
  ```
343
+ - `always_sign_api_key`: set to `True` to use HMAC-SHA256 signed authentication instead of header auth. When set, `header_authentication` automatically defaults to `False` — both methods do not fire simultaneously. The signing algorithm is identical to the standard API: `HMAC-SHA256(key, username + timestamp + path)`, with `timestamp` and `signature` sent as query parameters.
344
+ ```python
345
+ api = API(USERNAME, KEY, always_sign_api_key=True)
346
+ api.nod(after="-60")
347
+ # sends: api_username, timestamp, signature — no X-Api-Key header
348
+ ```
330
349
  - `output_format`: (choose either `csv` or `jsonl` - default is `jsonl`). Cannot be used in `domainrdap` feeds. Additionally, `csv` is not available for `download` endpoints.
331
350
  ```python
332
351
  api = API(USERNAME, KEY)
333
352
  api.nod(output_format="csv", **kwargs)
334
353
  ```
335
354
 
336
- The Feed API standard access pattern is to periodically request the most recent feed data, as often as every 60 seconds. Specify the range of data you receive in one of two ways:
355
+ The `feed` endpoint streams live NDJSON data. The standard access pattern is to poll as often as every 60 seconds. Specify the range of data you receive in one of two ways:
337
356
 
338
357
  1. With `sessionID`: Make a call and provide a new `sessionID` parameter of your choosing. The API will return the last hour of data by default.
339
358
  - Each subsequent call to the API using your `sessionID` will return all data since the last.
@@ -342,9 +361,75 @@ The Feed API standard access pattern is to periodically request the most recent
342
361
  - Either an `after=-60` query parameter, where (in this example) -60 indicates the previous 60 seconds.
343
362
  - Or `after` and `before` query parameters for a time range, with each parameter accepting an ISO-8601 UTC formatted timestamp (a UTC date and time of the format YYYY-MM-DDThh:mm:ssZ)
344
363
 
345
- ## Handling iterative response from RTUF endpoints:
364
+ The `download` endpoint returns a standard JSON response (not a stream) listing available S3 batch files. Time parameters (`sessionID`, `after`, `before`) are **not** required for download calls.
365
+
366
+ ```python
367
+ api = API(USERNAME, KEY)
368
+ result = api.nod(endpoint="download", limit=5)
369
+ print(result["download_name"])
370
+ for f in result["files"]:
371
+ print(f["name"], f["url"])
372
+ ```
373
+
374
+ ### Feed parameters
375
+
376
+ The feed methods accept the following parameters, grouped by purpose. Availability depends on the feed (see the notes below the table).
377
+
378
+ #### Session Management Parameters
379
+
380
+ - `sessionID`: A custom string used to distinguish between different sessions. Required when using `fromBeginning`.
381
+ - `after`: Start of the query window. Either an integer offset relative to now in seconds (e.g. `-60`), or an absolute ISO 8601 UTC datetime (`YYYY-MM-DDTHH:MM:SSZ`).
382
+ - `before`: End of the query window (inclusive). Either an integer from `-1` to `-432000` (seconds before now), or an absolute ISO 8601 UTC datetime. The query window covers at most the most recent 5 days; a value older than 5 days returns no records.
383
+ - `fromBeginning`: Boolean (`true`/`false`/`1`/`0`, default `false`). Requires a valid `sessionID`. When `true` on the first request of a new session, returns the first hour of data in the time window instead of the last. Using it with an existing `sessionID` returns an HTTP 406; using it without a `sessionID` or with a non-boolean value returns an HTTP 422.
384
+
385
+ ```python
386
+ api = API(USERNAME, KEY)
387
+ api.nod(sessionID="my-new-session-id", after=-3600, fromBeginning=True)
388
+ ```
389
+
390
+ #### Filter Parameters
391
+
392
+ - `domain`: Filter for an exact domain or a substring contained within a domain by prefixing or suffixing your substring with `*`.
393
+ - `overall_min`, `malware_min`, `phishing_min`, `spam_min`, `proximity_min`: Integer risk score thresholds (range `1` to `99`, optional). Available on the `realtime_domain_risk` and `domainhotlist` feeds only. When multiple are supplied they act as a logical AND — a domain must meet ALL specified thresholds to be returned.
394
+
395
+ ```python
396
+ api = API(USERNAME, KEY)
397
+ api.domainhotlist(after=-3600, overall_min=70, phishing_min=50)
398
+ ```
399
+
400
+ - IP feed filters (available on the `iprisk` and `iphotlist` feeds only). All are optional integers/strings and combine as a logical AND:
401
+ - Domain activity & volume: `pdns_resolutions_min`, `bad_pdns_resolutions_min` (positive integers, distinct/bad domains resolving to the IP in the last 24 hours) and `total_domains_max` (positive integer; caps total hosted domains to filter out superhosters like CDNs).
402
+ - Threat intelligence & combined risk percentages: `third_party_threats_min` (positive integer), plus `all_threats_combined_percent_min`, `combined_phishing_percent_min`, `combined_malware_percent_min`, `combined_spam_percent_min` (percentages `0` to `100` of hosted domains confirmed or predicted malicious).
403
+ - Confirmed threat percentages: `all_threats_percent_min`, `percent_phishing_min`, `percent_malware_min`, `percent_spam_min` (percentages `0` to `100` of hosted domains actively confirmed).
404
+ - Infrastructure & geolocation: `asn` (integer, digits only — no `AS` prefix or wildcards), `organization` (exact name, no wildcards) and `country_code` (case-sensitive two-letter code, e.g. `CN`, `US`, `NL`).
405
+
406
+ ```python
407
+ api = API(USERNAME, KEY)
408
+ api.iprisk(after=-3600, bad_pdns_resolutions_min=5, total_domains_max=1000, country_code="US")
409
+ ```
410
+
411
+ #### Result formatting parameters
412
+
413
+ - `output_format`: `csv` or `jsonl` (default `jsonl`). Not available on the `domainrdap` feed. `csv` is not available for `download` endpoints.
414
+ - `headers`: When `csv` output is used, adds a header row to the first line of the response.
415
+ - `top`: Positive integer from `1` to `1,000,000,000` limiting the number of results in the response payload. Ignored for the `download` endpoint.
416
+
417
+ #### Download-only parameters
418
+
419
+ These parameters are only accepted when `endpoint="download"`. They are ignored for the `feed` endpoint.
420
+
421
+ - `limit`: Maximum number of files to return in the response.
422
+ - `page`: Zero-indexed page of results to return. Available on `realtime_domain_risk`, `domainhotlist`, `iphotlist`, and `iprisk`.
423
+ - `prefix`: Filter files by date prefix (e.g. `"2026-08-"`). Available on `realtime_domain_risk`, `domainhotlist`, `iphotlist`, and `iprisk`.
424
+
425
+ ```python
426
+ api = API(USERNAME, KEY)
427
+ api.iphotlist(endpoint="download", limit=10, page=0, prefix="2026-08-")
428
+ ```
429
+
430
+ ## Handling iterative response from RTTF endpoints:
346
431
 
347
- Since we may dealing with large feeds datasets, the python wrapper uses `generator` for efficient memory handling. Therefore, we need to iterate through the `generator` if we're accessing the partial results of the feeds data.
432
+ Since we may be dealing with large feeds datasets, the python wrapper uses `generator` for efficient memory handling. Therefore, we need to iterate through the `generator` if we're accessing the partial results of the feeds data.
348
433
 
349
434
  ### Single request because the requested data is within the maximum result:
350
435
  ```python
@@ -253,6 +253,19 @@ Please see the [supported versions](https://github.com/DomainTools/python_api/ra
253
253
  for the DomainTools Python support policy.
254
254
 
255
255
 
256
+ Authentication
257
+ ===================
258
+
259
+ The wrapper supports two authentication modes, selected automatically based on the product:
260
+
261
+ | Product | Default method | Params sent |
262
+ |---|---|---|
263
+ | Standard API (Iris, Whois, etc.) | HMAC-SHA256 signed | `api_username`, `timestamp`, `signature` as query params |
264
+ | Real-Time Threat Feeds (RTTF) | Header authentication | `X-Api-Key` header |
265
+
266
+ RTTF feeds also support HMAC signing as an opt-in via `always_sign_api_key=True` — see the RTTF section below.
267
+
268
+
256
269
  Real-Time Threat Feeds
257
270
  ===================
258
271
 
@@ -264,18 +277,24 @@ Custom parameters aside from the common `GET` Request parameters:
264
277
  api = API(USERNAME, KEY)
265
278
  api.nod(endpoint="feed", **kwargs)
266
279
  ```
267
- - `header_authentication`: by default, we're using API Header Authentication. Set this False if you want to use API Key and Secret Authentication. Apparently, you can't use API Header Authentication for `download` endpoints so this will be defaulted to `False` even without explicitly setting it.
280
+ - `header_authentication`: by default, all RTTF endpoints (both `feed` and `download`) use API Header Authentication, sending the API key via the `X-Api-Key` header. Set this to `False` to pass the API key as a query parameter instead.
268
281
  ```python
269
282
  api = API(USERNAME, KEY, header_authentication=False)
270
283
  api.nod(**kwargs)
271
284
  ```
285
+ - `always_sign_api_key`: set to `True` to use HMAC-SHA256 signed authentication instead of header auth. When set, `header_authentication` automatically defaults to `False` — both methods do not fire simultaneously. The signing algorithm is identical to the standard API: `HMAC-SHA256(key, username + timestamp + path)`, with `timestamp` and `signature` sent as query parameters.
286
+ ```python
287
+ api = API(USERNAME, KEY, always_sign_api_key=True)
288
+ api.nod(after="-60")
289
+ # sends: api_username, timestamp, signature — no X-Api-Key header
290
+ ```
272
291
  - `output_format`: (choose either `csv` or `jsonl` - default is `jsonl`). Cannot be used in `domainrdap` feeds. Additionally, `csv` is not available for `download` endpoints.
273
292
  ```python
274
293
  api = API(USERNAME, KEY)
275
294
  api.nod(output_format="csv", **kwargs)
276
295
  ```
277
296
 
278
- The Feed API standard access pattern is to periodically request the most recent feed data, as often as every 60 seconds. Specify the range of data you receive in one of two ways:
297
+ The `feed` endpoint streams live NDJSON data. The standard access pattern is to poll as often as every 60 seconds. Specify the range of data you receive in one of two ways:
279
298
 
280
299
  1. With `sessionID`: Make a call and provide a new `sessionID` parameter of your choosing. The API will return the last hour of data by default.
281
300
  - Each subsequent call to the API using your `sessionID` will return all data since the last.
@@ -284,9 +303,75 @@ The Feed API standard access pattern is to periodically request the most recent
284
303
  - Either an `after=-60` query parameter, where (in this example) -60 indicates the previous 60 seconds.
285
304
  - Or `after` and `before` query parameters for a time range, with each parameter accepting an ISO-8601 UTC formatted timestamp (a UTC date and time of the format YYYY-MM-DDThh:mm:ssZ)
286
305
 
287
- ## Handling iterative response from RTUF endpoints:
306
+ The `download` endpoint returns a standard JSON response (not a stream) listing available S3 batch files. Time parameters (`sessionID`, `after`, `before`) are **not** required for download calls.
307
+
308
+ ```python
309
+ api = API(USERNAME, KEY)
310
+ result = api.nod(endpoint="download", limit=5)
311
+ print(result["download_name"])
312
+ for f in result["files"]:
313
+ print(f["name"], f["url"])
314
+ ```
315
+
316
+ ### Feed parameters
317
+
318
+ The feed methods accept the following parameters, grouped by purpose. Availability depends on the feed (see the notes below the table).
319
+
320
+ #### Session Management Parameters
321
+
322
+ - `sessionID`: A custom string used to distinguish between different sessions. Required when using `fromBeginning`.
323
+ - `after`: Start of the query window. Either an integer offset relative to now in seconds (e.g. `-60`), or an absolute ISO 8601 UTC datetime (`YYYY-MM-DDTHH:MM:SSZ`).
324
+ - `before`: End of the query window (inclusive). Either an integer from `-1` to `-432000` (seconds before now), or an absolute ISO 8601 UTC datetime. The query window covers at most the most recent 5 days; a value older than 5 days returns no records.
325
+ - `fromBeginning`: Boolean (`true`/`false`/`1`/`0`, default `false`). Requires a valid `sessionID`. When `true` on the first request of a new session, returns the first hour of data in the time window instead of the last. Using it with an existing `sessionID` returns an HTTP 406; using it without a `sessionID` or with a non-boolean value returns an HTTP 422.
326
+
327
+ ```python
328
+ api = API(USERNAME, KEY)
329
+ api.nod(sessionID="my-new-session-id", after=-3600, fromBeginning=True)
330
+ ```
331
+
332
+ #### Filter Parameters
333
+
334
+ - `domain`: Filter for an exact domain or a substring contained within a domain by prefixing or suffixing your substring with `*`.
335
+ - `overall_min`, `malware_min`, `phishing_min`, `spam_min`, `proximity_min`: Integer risk score thresholds (range `1` to `99`, optional). Available on the `realtime_domain_risk` and `domainhotlist` feeds only. When multiple are supplied they act as a logical AND — a domain must meet ALL specified thresholds to be returned.
336
+
337
+ ```python
338
+ api = API(USERNAME, KEY)
339
+ api.domainhotlist(after=-3600, overall_min=70, phishing_min=50)
340
+ ```
341
+
342
+ - IP feed filters (available on the `iprisk` and `iphotlist` feeds only). All are optional integers/strings and combine as a logical AND:
343
+ - Domain activity & volume: `pdns_resolutions_min`, `bad_pdns_resolutions_min` (positive integers, distinct/bad domains resolving to the IP in the last 24 hours) and `total_domains_max` (positive integer; caps total hosted domains to filter out superhosters like CDNs).
344
+ - Threat intelligence & combined risk percentages: `third_party_threats_min` (positive integer), plus `all_threats_combined_percent_min`, `combined_phishing_percent_min`, `combined_malware_percent_min`, `combined_spam_percent_min` (percentages `0` to `100` of hosted domains confirmed or predicted malicious).
345
+ - Confirmed threat percentages: `all_threats_percent_min`, `percent_phishing_min`, `percent_malware_min`, `percent_spam_min` (percentages `0` to `100` of hosted domains actively confirmed).
346
+ - Infrastructure & geolocation: `asn` (integer, digits only — no `AS` prefix or wildcards), `organization` (exact name, no wildcards) and `country_code` (case-sensitive two-letter code, e.g. `CN`, `US`, `NL`).
347
+
348
+ ```python
349
+ api = API(USERNAME, KEY)
350
+ api.iprisk(after=-3600, bad_pdns_resolutions_min=5, total_domains_max=1000, country_code="US")
351
+ ```
352
+
353
+ #### Result formatting parameters
354
+
355
+ - `output_format`: `csv` or `jsonl` (default `jsonl`). Not available on the `domainrdap` feed. `csv` is not available for `download` endpoints.
356
+ - `headers`: When `csv` output is used, adds a header row to the first line of the response.
357
+ - `top`: Positive integer from `1` to `1,000,000,000` limiting the number of results in the response payload. Ignored for the `download` endpoint.
358
+
359
+ #### Download-only parameters
360
+
361
+ These parameters are only accepted when `endpoint="download"`. They are ignored for the `feed` endpoint.
362
+
363
+ - `limit`: Maximum number of files to return in the response.
364
+ - `page`: Zero-indexed page of results to return. Available on `realtime_domain_risk`, `domainhotlist`, `iphotlist`, and `iprisk`.
365
+ - `prefix`: Filter files by date prefix (e.g. `"2026-08-"`). Available on `realtime_domain_risk`, `domainhotlist`, `iphotlist`, and `iprisk`.
366
+
367
+ ```python
368
+ api = API(USERNAME, KEY)
369
+ api.iphotlist(endpoint="download", limit=10, page=0, prefix="2026-08-")
370
+ ```
371
+
372
+ ## Handling iterative response from RTTF endpoints:
288
373
 
289
- Since we may dealing with large feeds datasets, the python wrapper uses `generator` for efficient memory handling. Therefore, we need to iterate through the `generator` if we're accessing the partial results of the feeds data.
374
+ Since we may be dealing with large feeds datasets, the python wrapper uses `generator` for efficient memory handling. Therefore, we need to iterate through the `generator` if we're accessing the partial results of the feeds data.
290
375
 
291
376
  ### Single request because the requested data is within the maximum result:
292
377
  ```python
@@ -0,0 +1 @@
1
+ 2.10.0
@@ -20,4 +20,4 @@ OTHER DEALINGS IN THE SOFTWARE.
20
20
 
21
21
  """
22
22
 
23
- current = "2.9.0"
23
+ current = "2.10.0"