{"id":8127,"library":"domaintools-api","title":"DomainTools Python API","description":"The DomainTools Python API Wrapper provides a unified interface to interact with DomainTools' cybersecurity and threat intelligence products, including Iris Investigate, Iris Enrich, Iris Detect, Lookups, Monitors, and Threat Feeds. It is actively maintained, currently at version 2.7.3, with frequent minor releases addressing bugs and ensuring API parity.","status":"active","version":"2.7.3","language":"en","source_language":"en","source_url":"https://github.com/domaintools/python_api","tags":["security","domain-intelligence","threat-intelligence","api-client","cybersecurity","whois"],"install":[{"cmd":"pip install domaintools-api --upgrade","lang":"bash","label":"Install or upgrade"}],"dependencies":[{"reason":"Requires Python 3.9 or newer.","package":"python","optional":false}],"imports":[{"note":"The primary API client is exposed as `API` from the `domaintools` package. A separate, older `domaintools` package exists on PyPI (last updated 2018) for domain parsing, which can cause import conflicts if installed incorrectly or simultaneously.","wrong":"import domaintools","symbol":"API","correct":"from domaintools import API"}],"quickstart":{"code":"import os\nfrom domaintools import API\n\n# Best practice: Store credentials securely in environment variables\nusername = os.environ.get('DOMAINTOOLS_USERNAME', 'your_username')\napi_key = os.environ.get('DOMAINTOOLS_API_KEY', 'your_api_key')\n\n# Initialize the API client\ntry:\n    api = API(username, api_key)\n\n    # Example 1: Domain Profile lookup\n    profile_result = api.domain_profile('example.com')\n    print(f\"Domain Profile for example.com: {profile_result.response()['domain']}\")\n\n    # Example 2: Iris Enrich\n    enrich_result = api.iris_enrich('domaintools.com')\n    for domain_data in enrich_result.response().get('results', {}):\n        print(f\"Enriched domain: {domain_data['domain']}, Risk Score: {domain_data['domain_risk']['risk_score']}\")\n        break # Just print one for brevity\n\n    # Example 3: Real-Time Threat Feed (New Observed Domains - NOD)\n    # Threat Feeds automatically use header authentication, handled by SDK\n    # Use sessionID for pagination or 'after' parameter for time range\n    nod_feed = api.nod(after=-3600) # Last hour\n    for record_json in nod_feed.response():\n        # Records are JSON strings, need to parse them\n        import json\n        record = json.loads(record_json)\n        print(f\"New observed domain from feed: {record['domain']}\")\n        break # Just print one for brevity\n\nexcept Exception as e:\n    print(f\"An error occurred: {e}\")\n    if username == 'your_username' or api_key == 'your_api_key':\n        print(\"Please set DOMAINTOOLS_USERNAME and DOMAINTOOLS_API_KEY environment variables or replace placeholders.\")","lang":"python","description":"Initialize the API client with your DomainTools username and API key. The library supports various DomainTools API endpoints, including Domain Profile lookups, Iris Enrich, and Real-Time Threat Feeds. Credentials should ideally be stored as environment variables (DOMAINTOOLS_USERNAME, DOMAINTOOLS_API_KEY) for security."},"warnings":[{"fix":"Ensure you are initializing the API client with your `username` and `api_key`. The SDK manages the correct authentication method internally for different endpoints. If manually configuring, consult documentation for specific endpoint requirements.","message":"The SDK primarily uses HMAC-signed authentication by default, which is the most secure method. However, Threat Feeds (e.g., NOD, NAD) automatically switch to header-based authentication. The SDK handles this transparently, but it's important context for custom authentication logic or troubleshooting.","severity":"gotcha","affected_versions":"All versions"},{"fix":"For most use cases, append `.response()` to your API call to get the core data. Example: `results = api.iris_enrich('domaintools.com').response()`.","message":"API calls return a response object. To access the raw data returned by DomainTools, use `.data()`. To get the actionable response content (typically a dictionary or list), use `.response()`. Direct attribute access (e.g., `profile['field']`) is also possible for specific results.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Implement exponential backoff and retry logic when receiving `503 Service Unavailable` errors. Review your DomainTools subscription limits if this occurs frequently.","message":"The library handles API rate limiting automatically. However, if a `503 Service Unavailable` error is received, it typically indicates a temporary service unavailability or a hard rate limit hit.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Upgrade to `domaintools-api` version 2.6.1 or newer: `pip install domaintools-api --upgrade`.","message":"Versions prior to 2.6.1 had an issue where `Accept-Encoding` was not set to 'identity', causing decompression issues with `iter_lines` for streaming RTTF endpoints. This could lead to malformed or incomplete streaming data.","severity":"breaking","affected_versions":"< 2.6.1"},{"fix":"Upgrade to `domaintools-api` version 2.7.3 or newer to ensure correct `risk_score` retrieval: `pip install domaintools-api --upgrade`.","message":"In versions prior to 2.7.3, the `iris_investigate` function would occasionally return missing `risk_score` values even when expected.","severity":"breaking","affected_versions":"< 2.7.3"}],"env_vars":null,"last_verified":"2026-04-16T00:00:00.000Z","next_check":"2026-07-15T00:00:00.000Z","problems":[{"fix":"Ensure you explicitly install `domaintools-api` and uninstall any conflicting `domaintools` package: `pip uninstall domaintools` (if present) then `pip install domaintools-api --upgrade`.","cause":"You might have installed a different, unrelated `domaintools` package (for domain parsing) instead of `domaintools-api`, or both are installed causing conflicts.","error":"ModuleNotFoundError: No module named 'domaintools' OR ImportError: cannot import name 'API' from 'domaintools'"},{"fix":"Verify your `DOMAINTOOLS_USERNAME` and `DOMAINTOOLS_API_KEY` environment variables or the credentials passed to `API()`. Double-check for typos and ensure your API key is still active in your DomainTools account portal.","cause":"The provided API username or key is incorrect, expired, or missing.","error":"domaintools.exceptions.NotAuthorizedException: 401 Unauthorized"},{"fix":"Contact your DomainTools account manager or support to verify that your subscription includes access to the specific API product (e.g., Iris Investigate, Iris Enrich, a particular Threat Feed) you are trying to use.","cause":"Your DomainTools account does not have the necessary permissions or subscription level to access the requested API endpoint or product.","error":"domaintools.exceptions.ForbiddenException: 403 Forbidden"},{"fix":"If `sessionID` was used, repeat the same request with the same `sessionID` to retrieve the next tranche of data until a `200 OK` response is received. The SDK's iteration over feed results typically handles this automatically, but be aware of it if manually processing responses.","cause":"For certain large data endpoints, especially Threat Feeds, the API may return partial results with a 206 status code, indicating that more data is available.","error":"HTTP 206 Partial Content"}]}