Domain Activity API v2

Python Command Line Tool

A simple ready-to-use Python script for querying the Domain Activity API v2 from your terminal or downloading its daily ZIP files. Useful for quick lookups, scheduled tasks, and testing your API credentials before building a full integration.

Requires Python 3 with the requests package. Install it with pip install requests. The script uses Python's built-in datetime module; no separate DateTime package is required. Python 3 on Windows can be installed via the Microsoft Store.

Setup

Download dnfeed-app.py and open it in a text editor. Replace the two placeholder values with your API credentials:

apikey    = "paste_your_api_key_here"
apisecret = "paste_your_api_secret_here"

Save the file and open a terminal. Change to the folder where you saved it.

Usage Examples

Added domains containing keywords — last 10 days

python dnfeed-app.py -d 10 -kw "%hdmi%,%interface%"

Deleted domains with the same keywords

python dnfeed-app.py -s deleted -d 10 -kw "%hdmi%,%interface%"

Domain names only, no dates

python dnfeed-app.py -d 10 -kw "%hdmi%,%interface%" -od

Filter by TLD

python dnfeed-app.py -d 5 -kw "%apple%" -tlds com,net,org

Download previous day's added domains ZIP

python dnfeed-app.py -zip

Download previous day's deleted domains ZIP

python dnfeed-app.py -zip -s deleted

Download ZIP for a specific date

python dnfeed-app.py -zip -zd 20240101
python dnfeed-app.py -zip -zd 20240101 -s deleted -o deleted-jan1.zip

Download current day's ZIP (domains processed so far)

python dnfeed-app.py -zip -latest
python dnfeed-app.py -zip -latest -s deleted

Show all available options

python dnfeed-app.py -h

All Options

Domain query options

-d days

Number of preceding days to retrieve data for. Between 0 and 14. Default 1. The 14-day ceiling is an intentional convenience limit in this sample client; the API's retained Domain Activity date window can vary from 14 to 45 days.

-kw keywords

Keyword filter. Separate multiple CLI keywords with commas; the script converts them to the API v2 | OR syntax. Use % as a wildcard and enclose the value in double quotes when using it.

-tlds tlds

Filter by TLD. Separate multiple TLDs with commas. Example: com,net,org

-s source

Data source. Use added or deleted. Default added. Applies to both query and ZIP download modes.

-od

Print domain names only, without dates.

-raw

Print the returned data rows as formatted JSON instead of the normal line output. Overrides -od.

-v

Accepted by the script for query-information output. The current script already shows query information by default.

Query mode makes one API request with limit=5000. If a search matches more than 5000 domains, use the API v2 pagination parameters directly to retrieve additional pages.

If you need history beyond the sample client's 14-day query window, use the API v2 date filters directly while the required dates remain available. Applications that need a longer-running corpus should collect and retain the daily feed locally.

ZIP download options

-zip

Switch to ZIP download mode. Downloads the full daily domain list as a ZIP file instead of querying. Use with -s to choose added or deleted.

-zd date

Date for the ZIP download in yyyymmdd format. Omit for the previous day's data. Cannot be used together with -latest.

-latest

Download a ZIP for the current day containing domains processed so far. Only valid without -zd.

-o filename

Output filename for the downloaded ZIP. Default is dnfeed-<source>-<label>.zip, where the label is prev, latest, or the date supplied with -zd.

The Code

Download dnfeed-app.py

import requests
import sys
import json
import datetime

# Provide your keys
apikey    = "paste_your_api_key_here"
apisecret = "paste_your_api_secret_here"

apiurl = "https://api.codepunch.com/dnfeed/v2/"


def get_api_token():
    AUTHURLFMT = "{}auth/{}/{}"
    url = AUTHURLFMT.format(apiurl, apikey, apisecret)
    response = requests.get(url)
    if response.status_code == 200:
        apiresponse = response.json()
    else:
        raise Exception('Authentication: {}'.format(response.status_code))
    if bool(apiresponse['status']) != True:
        raise Exception(apiresponse['error'])
    return apiresponse['token']


def get_api_data(token, command, parameters):
    APIURLFMT = "{}{}/{}/"
    url = APIURLFMT.format(apiurl, token, command)
    response = requests.get(url, params=parameters)
    if response.status_code != 200:
        raise Exception('Invalid response code: {}'.format(response.status_code))
    apiresponse = response.json()
    if bool(apiresponse['status']) != True:
        raise Exception(apiresponse['error'])
    return apiresponse


def get_api_zip_file(token, command, parameters, filename):
    APIURLFMT = "{}{}/{}/"
    url = APIURLFMT.format(apiurl, token, command)
    response = requests.get(url, params=parameters, stream=True)
    totalbits = 0
    if response.status_code == 200:
        with open(filename, 'wb') as f:
            for chunk in response.iter_content(chunk_size=1024):
                if chunk:
                    totalbits += 1024
                    f.write(chunk)
        print("Downloaded {} KB to {}".format(round(totalbits / 1024), filename))
    else:
        raise Exception('Invalid response code: {}'.format(response.status_code))


try:
    idx               = 0
    source_name       = 'added'
    keywords          = "%apple%|%paypal%"
    number_of_days    = 1
    only_domain_names = False
    show_information  = True
    tlds_to_get       = ''
    show_raw_data     = False
    download_zip      = False
    zip_date          = None
    zip_latest        = False
    zip_output_file   = None

    for i in sys.argv:
        if   i == "-h":
            print("\ndnfeed-app.py [options]\n")
            print("Domain query options:")
            print("  -d days      Number of preceding days (0-14)")
            print("               Sample-client limit; API retention can vary from 14-45 days.")
            print("  -kw keywords Keywords. Separate with comma. Use % for wildcard.")
            print("  -tlds tlds   TLDs to filter. Separate with commas")
            print("  -s source    added or deleted. Default: added")
            print("  -od          Domain names only")
            print("  -raw         Returned data rows as formatted JSON")
            print("  -v           Query information (already shown by default)")
            print("\nZIP download options:")
            print("  -zip         Download daily ZIP instead of querying")
            print("  -zd date     Date in yyyymmdd format. Omit for previous day; cannot use with -latest.")
            print("  -latest      Current day ZIP (processed so far). Cannot use with -zd.")
            print("  -o filename  Output filename. Default: dnfeed-<source>-<label>.zip")
            sys.exit()
        elif i == "-d":      idx += 1; number_of_days    = min(int(sys.argv[idx]), 14)
        elif i == "-kw":     idx += 1; keywords          = sys.argv[idx].replace(",", "|")
        elif i == "-tlds":   idx += 1; tlds_to_get        = sys.argv[idx]
        elif i == "-s":      idx += 1; source_name        = sys.argv[idx]
        elif i == "-od":     only_domain_names  = True;  idx += 1
        elif i == "-raw":    show_raw_data      = True;  idx += 1
        elif i == "-v":      show_information   = True;  idx += 1
        elif i == "-zip":    download_zip       = True;  idx += 1
        elif i == "-zd":     idx += 1; zip_date           = sys.argv[idx]
        elif i == "-latest": zip_latest         = True;  idx += 1
        elif i == "-o":      idx += 1; zip_output_file    = sys.argv[idx]
        else:               idx += 1

    if source_name not in ('added', 'deleted'):
        source_name = 'added'

    token = get_api_token()

    # ── ZIP download mode ──────────────────────────────────────────────────
    if download_zip:
        if zip_latest and zip_date:
            raise Exception("Use either -zd or -latest, not both.")
        params = {"source": source_name}
        if zip_date:
            params["date"] = zip_date;  label = zip_date
        elif zip_latest:
            params["latest"] = "";     label = "latest"
        else:
            label = "prev"
        if zip_output_file is None:
            zip_output_file = "dnfeed-{}-{}.zip".format(source_name, label)
        print("Downloading {} {} ZIP to {}...".format(source_name, label, zip_output_file))
        get_api_zip_file(token, "dailyzip", params, zip_output_file)

    # ── Domain query mode ──────────────────────────────────────────────────
    else:
        today_date = datetime.datetime.now()
        olddate    = today_date - datetime.timedelta(days=number_of_days)
        datecode   = olddate.strftime("%Y%m%d")
        if show_information:
            print('\nGetting domain data after {} for keywords \'{}\' from {} list.\n'.format(
                olddate.strftime("%Y-%m-%d"), keywords, source_name))
        parameters = {"date": datecode, "limit": 5000, "kw": keywords,
                      "dcm": 'gte', "tlds": tlds_to_get, "format": "json"}
        thedata    = get_api_data(token, source_name, parameters)
        if show_raw_data:
            print(json.dumps(thedata['data'], indent=2))
        else:
            for d in thedata['data']:
                print(d['domain'] if only_domain_names else d['domain'] + ', ' + d['date'])

except Exception as e:
    print(repr(e))

Need help with the API? See the full API v2 documentation or contact us with your subscription details.