Using the GreyNoise Enterprise API

API Information

  • API Address: api.greynoise.io
  • Requires an active Subscription or Enterprise Trial to Access
  • Not available to Community Users (Community Users should see the Using the GreyNoise Community API

GreyNoise API Fundamentals

While typical to use, usage of GreyNoise REST API should follow these fundamental rules, based of the use case or intended usage:

  • Use the GreyNoise SDK when possible
  • For High-Volume usage use the multi-quick API endpoint primarily to tag IPs in bulk as noise or not, then use the Context API endpoint for full IP details downstream
  • For Low-Volume usage, check first the RIOT API endpoint, then if the IP is not part of the RIOT project, check the Context API endpoint
  • For additional information on TAGS returned from the context API, use the METADATA TAGS endpoint. This endpoint returns all known tags with metadata, so it is recommended to cache this data locally, if possible, on a daily basis and add the tag metadata to any context lookups performed.

PostMan Collection

The Full Set of API endpoints and parameters can be referenced here:

Run In Postman

Basic IP Lookup Flow

  • Query the Quick IP API Endpoint. If the IP is marked as noise, continue, otherwise stop and return code information
  • Query the Context IP API Endpoint. This will return all the details collected for the given IP. If the additional metadata on any TAGS returned on the IP is desired, continue, otherwise present the data.
from greynoise import GreyNoise

session = GreyNoise(api_key='<api_key>', integration_name="sdk-sample")
ip_address = '195.72.230.190'
output = []

quick_response = session.quick(ip_address)

for result in quick_response:
    if result['noise']:
        context_response = session.ip(ip_address)
        context_response['noise'] = result['noise']
        context_response['code'] = result['code']
        context_response['code_message'] = result['code_message']
        context_response['visualizer_url'] = 'https://viz.greynoise.io/ip/' + str(ip_address)
        output.append(context_response)
    else:
        output.append(result)

print(output)
import requests

context_url = "https://api.greynoise.io/v2/noise/context/"
quick_url = "https://api.greynoise.io/v2/noise/quick/"

headers = {"key": "<api_key>", "Accept": "application/json",
           "User-Agent": "sample-python-script"}

code_messages = {
    "0x00": "IP has never been observed scanning the Internet",
    "0x01": "IP has been observed by the GreyNoise sensor network",
    "0x02": (
        "IP has been observed scanning the GreyNoise sensor network, "
        "but has not completed a full connection, meaning this can be spoofed"
    ),
    "0x03": (
        "IP is adjacent to another host that has been directly observed "
        "by the GreyNoise sensor network"
    ),
    "0x04": "RESERVED",
    "0x05": "IP is commonly spoofed in Internet-scan activity",
    "0x06": (
        "IP has been observed as noise, but this host belongs to a cloud provider "
        "where IPs can be cycled frequently"
    ),
    "0x07": "IP is invalid",
    "0x08": (
        "IP was classified as noise, but has not been observed "
        "engaging in Internet-wide scans or attacks in over 60 days"
    ),
    "0x09": "IP was found in RIOT",
    "0x10": "IP has been observed by the GreyNoise sensor network and is in RIOT",
    "404": "IP is Invalid",
}

ip_address = '186.33.111.236'
output = {}

quick_response = requests.get(quick_url + ip_address, headers=headers)
quick_json = quick_response.json()
quick_json["code_message"] = code_messages[quick_json["code"]]

if quick_json['noise']:
    context_response = requests.get(context_url + ip_address, headers=headers)
    context_json = context_response.json()
    context_json['noise'] = quick_json['noise']
    context_json['code'] = quick_json['code']
    context_json['code_message'] = quick_json['code_message']
    context_json['visualizer_url'] = 'https://www.greynoise.io/viz/ip/' + str(ip_address)
    output = context_json
else:
    output = quick_json

print(output)

Advanced IP Lookup Flow

When creating an advanced lookup for a single IP, the following flow and logic are recommended:

  • Query the Quick IP API Endpoint.
  • If the Quick endpoint includes "riot: true", query the RIOT API endpoint and present the data
  • if the Quick endpoint includes "noise: true", query the Context API endpoint and present the data
  • If additional details on tags associated with an IP found in the Context endpoint are desired, query the Metadata TAGS API Endpoint, and return the matching TAG meta data for the TAGS found on the IP Context lookup.
from greynoise import GreyNoise

session = GreyNoise(api_key='<api_key>', integration_name="sdk-sample")
ip_address = '195.72.230.190'
output = []


def build_tag_details(metadata, tags):
    detailed_tags = []
    for tag in tags:
        for detailed_tag in metadata["metadata"]:
            if tag == detailed_tag["name"]:
                detailed_tags.append(detailed_tag)
    return detailed_tags


quick_response = session.quick(ip_address)

for result in quick_response:
    context_response = {}
    riot_response = {}
    if result['noise']:
        context_response = session.ip(ip_address)
        tags_response = session.metadata()
        updated_tags = build_tag_details(tags_response, context_response["tags"])
        context_response.pop("tags")
        context_response["tags"] = updated_tags
    if result['riot']:
        riot_response = session.riot(ip_address)

    if context_response and riot_response:
        response = context_response.copy()
        response.update(riot_response)
        output.append(response)
    elif context_response:
        output.append(context_response)
    elif riot_response:
        output.append(riot_response)
    else:
        output.append(result)

print(output)
import requests

context_url = "https://api.greynoise.io/v2/noise/context/"
quick_url = "https://api.greynoise.io/v2/noise/quick/"
riot_url = "https://api.greynoise.io/v2/riot/"
metadata_url = "https://api.greynoise.io/v2/meta/metadata"

headers = {"key": "<api_key>", "Accept": "application/json",
           "User-Agent": "sample-python-script"}

ip_address = '186.33.111.236'
output = []


def build_tag_details(metadata, tags):
    detailed_tags = []
    for tag in tags:
        for detailed_tag in metadata["metadata"]:
            if tag == detailed_tag["name"]:
                detailed_tags.append(detailed_tag)
    return detailed_tags


quick_response = requests.get(quick_url + ip_address, headers=headers)

context_json = {}
riot_json = {}
if quick_response.json()['noise']:
    context_response = requests.get(context_url + ip_address, headers=headers)
    context_json = context_response.json()
    if len(context_json["tags"]) >= 1:
        tags_response = requests.get(metadata_url, headers=headers)
        tags_json = tags_response.json()
        updated_tags = build_tag_details(tags_json, context_json["tags"])
        context_json.pop("tags")
        context_json["tags"] = updated_tags
if quick_response.json()['riot']:
    riot_response = requests.get(riot_url + ip_address, headers=headers)
    riot_json = riot_response.json()

if context_json and riot_json:
    response = context_json.copy()
    response.update(riot_json)
    output.append(response)
elif context_json:
    output = context_json
elif riot_json:
    output = riot_json
else:
    output = quick_response.json()

print(output)

High Volume IP Lookups

When integration with the GreyNoise REST API to due high volume lookup of IPs, it is strongly recommended that the Multi-Quick API Endpoint be used to provide quick lookup data on IPs in bulk. The Multi-Quick endpoint supports either a GET (which is limited to 500 IPs per request) or a POST (which is limited to 1000 IPs per request).

The Quick lookup will allow each requested IP to be tagged as "NOISE", "RIOT", both or neither. For those tagged at "NOISE", which means that GreyNoise has observed the IP scanning the internet, a secondary Context API Endpoint lookup can be done for each IP as a downstream task. For those tagged as "RIOT", which means that the IP is part of the current known services list, a secondary RIOT API ENdpoint lookup can be down for each IP as a downstream task.

import requests

bulk_quick_url = "https://api.greynoise.io/v2/noise/multi/quick"

headers = {"key": "<api-key>", "Accept": "application/json",
           "User-Agent": "sample-python-script"}

ip_addresses = ["1.2.3.4", "5.6.7.8", "8.8.8.8", "123.123.123.123"]
output = {}

payload = {"ips": list(ip_addresses)}
quick_response = requests.post(bulk_quick_url, headers=headers, json=payload)

print(quick_response.json())
[
    {
        "ip": "69.112.115.157",
        "noise": false,
        "riot" : false,
        "code": "0x00"
    },
    {
        "ip": "123.179.144.49",
        "noise": false,
        "riot" : false,
        "code": "0x00"
    },
    {
        "ip": "74.126.110.231",
        "noise": false,
        "riot" : false,
        "code": "0x00"
    }
]

Query (GNQL) API Sample

The GreyNoise GNQL API endpoint allows search through the GreyNoise NOISE dataset. The endpoint will take in any query in the GNQL format and provide an output of all of the IPs in the NOISE dataset that match the query.

By default, the endpoint will return 1,000 results per page. The size parameter can be used to up the page limit to 10,000 results per page. When the total number of results exceeds the page size, a scroll token is provided in response to fetch the next set of results.

Here is a sample script for using the GreyNoise SDK to cycle through the GNQL response:

from greynoise import GreyNoise

session = GreyNoise(api_key="<api-key>", integration_name="gnql-sample")

response = session.query("last_seen:1d tags:Mirai")
data = response["data"]

scroll = response['scroll']
while scroll:
    response = session.query("last_seen:1d", scroll=scroll)
    data.extend(response["data"])
    scroll = (response['scroll'] if 'scroll' in response else False)

API Responses

IP Context Lookup Responses

{
  "actor": "Alpha Strike Labs",
	"bot": false,
	"classification": "benign",
	"cve": [],
	"first_seen": "2019-07-29",
	"ip": "45.83.66.207",
	"last_seen": "2021-03-30",
	"metadata": {
		"asn": "AS208843",
		"category": "business",
		"city": "Hamburg",
		"country": "Germany",
		"country_code": "DE",
		"organization": "Alpha Strike Labs GmbH",
		"os": "Linux 2.2-3.x",
		"rdns": "",
		"region": "Hamburg",
		"tor": false
	},
		"raw_data": {
		"hassh": [
			{
				"fingerprint": "084386fa7ae5039bcf6f07298a05a227",
				"port": 22
			}
		],
		"ja3": [
			{
				"fingerprint": "bfa64571e2d6cadd2708d867ae17eab7",
				"port": 443
			}
		],
		"scan": [
			{
				"port": 21,
				"protocol": "TCP"
			},
			{
				"port": 22,
				"protocol": "TCP"
			}
		],
		"web": {
			"paths": [
				"/"
			],
			"useragents": [
				"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:65.0) Gecko/20100101 Firefox/65.0"
			]
			}
		},
		"seen": true,
		"spoofable": false,
		"tags": [
			"Tridium NiagraAX Fox ICS Scanner",
			"Web Crawler"
		],
		"vpn": false,
		"vpn_service": ""
}
{
	'error': 'Invalid IP submitted'
}
{
	'message': 'Forbidden'
}

IP Quick Lookup Responses

{
  'ip': '45.83.66.207', 
  'noise': True, 
  "riot" : false,
  'code': '0x01'
}
{
  'code': '0x07', 
  'error': 'invalid ip address submitted'
}
{
  'message': 'Forbidden'
}

IP RIOT Lookup Responses

{
  'ip': '8.8.8.8', 
  'riot': True, 
  'category': 'public_dns', 
  'name': 'Google Public DNS', 
  'description': "Google's global domain name system (DNS) resolution service.", 
  'explanation': "Public DNS services are used as alternatives to ISP's name servers. You may see devices on your network communicating with Google Public DNS over port 53/TCP or 53/UDP to resolve DNS lookups.", 
  'last_updated': '2021-04-01T09:55:51Z', 
  'logo_url': 'https://www.gstatic.com/devrel-devsite/prod/v9d82702993bc22f782b7874a0f933b5e39c1f0889acab7d1fce0d6deb8e0f63d/cloud/images/cloud-logo.svg', 
  'reference': 'https://developers.google.com/speed/public-dns/docs/isp#alternative'
  'trust_level': "1"
}
{
  'message': 'IP provided is not a routable IPv4 address'
}
{
  'message': 'Forbidden'
}
{
  'ip': '45.83.66.207', 
  'riot': False
}

GNQL Query

{
  'complete': False, 
  'count': 371885, 
  'data': [
    {
      'ip': '88.241.161.210', 
      'metadata': {
        'asn': 'AS9121', 
        'city': 'Istanbul', 
        'country': 'Turkey', 
        'country_code': 'TR', 
        'organization': 'Turk Telekomunikasyon Anonim Sirketi', 
        'category': 'isp', 
        'tor': False, 
        'rdns': '88.241.161.210.dynamic.ttnet.com.tr', 
        'os': 'Linux 2.2-3.x', 
        'region': 'Istanbul'
      }, 
      'bot': False, 
      'vpn': False, 
      'vpn_service': '', 
      'spoofable': False, 
      'raw_data': {
        'scan': [
          {
            'port': 80, 
            'protocol': 'TCP'
          }],
        'web': {
          'paths': ['/'],
          'useragents': [
            'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:25.0) Gecko/20100101 Firefox/25.0'
          ]}, 
        'ja3': [], 
        'hassh': []
      }, 
      'first_seen': '2021-04-01', 
      'last_seen': '2021-04-01', '
      'rdns': '88.241.161.210.dynamic.ttnet.com.tr', 
      'seen': True, 
      'tags': ['Web Crawler'], 
      'actor': 'unknown', 
      'classification': 'unknown', 
      'cve': []
    }, 
    {
      'ip': '107.174.17.141', 
      'metadata': {
      	'asn': 'AS20278', 
      	'city': 'Buffalo', 
      	'country': 'United States', 
      	'country_code': 'US', 
      	'organization': 'Nexeon Technologies, Inc.',
      	'category': 'isp', 
      	'tor': False, 
      	'rdns': '', 
      	'os': 'unknown', 
      	'region': 'New York'
      }, 
      'bot': False, 
      'vpn': True, 
      'vpn_service': 'NORD_VPN', 
      'spoofable': True, 
      'raw_data': {
        'scan': [{
            'port': 55359, 
            'protocol': 'UDP'}], 
        'web': {}, 
        'ja3': [], 
        'hassh': []
      }, 
      'first_seen': '2021-04-01', 
      'last_seen': '2021-04-01', 
      'rdns': '', 
      'seen': True, 
      'tags': [], 
      'actor': 'unknown', 
      'classification': 'unknown', 
      'cve': []}], 
	'message': 'ok', 
  'query': 'last_seen:1d', 
  'scroll': 'DnF1ZXJ5VGhlbkZldGNoBQAAAAAFvC7hFkFKSExEdUc4VEtta2syaGg2R3kzNGcAAAAABbwu4hZBSkhMRHVHOFRLbWtrMmhoNkd5MzRnAAAAAAW8LuMWQUpITER1RzhUS21razJoaDZHeTM0ZwAAAAAFvC7kFkFKSExEdUc4VEtta2syaGg2R3kzNGcAAAAABbwu5RZBSkhMRHVHOFRLbWtrMmhoNkd5MzRn'
}
{
  'message': 'Forbidden'
}