Scroll to navigation



shodan-python - shodan-python Documentation

This is the official Python wrapper around both the Shodan REST API as well as the experimental Streaming API. And as a bonus it also lets you search for exploits using the Shodan Exploits REST API. If you’re not sure where to start simply go through the “Getting Started” section of the documentation and work your way down through the examples.

For more information about Shodan and how to use the API please visit our official help center at:


Getting Started


To get started with the Python library for Shodan, first make sure that you’ve received your API key. Once that’s done, install the library via the cheeseshop using:

$ easy_install shodan

Or if you already have it installed and want to upgrade to the latest version:

$ easy_install -U shodan

It’s always safe to update your library as backwards-compatibility is preserved. Usually a new version of the library simply means there are new methods/ features available.

Connect to the API

The first thing we need to do in our code is to initialize the API object:

import shodan
SHODAN_API_KEY = "insert your API key here"
api = shodan.Shodan(SHODAN_API_KEY)

Searching Shodan

Now that we have our API object all good to go, we’re ready to perform a search:

# Wrap the request in a try/ except block to catch errors

# Search Shodan
results ='apache')
# Show the results
print('Results found: {}'.format(results['total']))
for result in results['matches']:
print('IP: {}'.format(result['ip_str']))
print('') except shodan.APIError as e:
print('Error: {}'.format(e))

Stepping through the code, we first call the method on the api object which returns a dictionary of result information. We then print how many results were found in total, and finally loop through the returned matches and print their IP and banner. Each page of search results contains up to 100 results.

There’s a lot more information that gets returned by the function. See below for a shortened example dictionary that returns:


'total': 8669969,
'matches': [
'data': 'HTTP/1.0 200 OK\r\nDate: Mon, 08 Nov 2010 05:09:59 GMT\r\nSer...',
'hostnames': [''],
'ip': 3579573318,
'ip_str': '',
'os': 'FreeBSD 4.4',
'port': 80,
'timestamp': '2014-01-15T05:49:56.283713'
] }

Please visit the REST API documentation for the complete list of properties that the methods can return.

It’s also good practice to wrap all API requests in a try/ except clause, since any error will raise an exception. But for simplicity’s sake, I will leave that part out from now on.

Looking up a host

To see what Shodan has available on a specific IP we can use the function:

# Lookup the host
host ='')
# Print general info

IP: {}
Organization: {}
Operating System: {} """.format(host['ip_str'], host.get('org', 'n/a'), host.get('os', 'n/a'))) # Print all banners for item in host['data']:
Port: {}
Banner: {}
""".format(item['port'], item['data']))


#!/usr/bin/env python
# Search SHODAN and print a list of IPs matching the query
# Author: achillean
import shodan
import sys
# Configuration
# Input validation
if len(sys.argv) == 1:

print 'Usage: %s <search query>' % sys.argv[0]
sys.exit(1) try:
# Setup the api
api = shodan.Shodan(API_KEY)
# Perform the search
query = ' '.join(sys.argv[1:])
result =
# Loop through the matches and print each IP
for service in result['matches']:
print service['ip_str'] except Exception as e:
print 'Error: %s' % e

Collecting Summary Information using Facets

A powerful ability of the Shodan API is to get summary information on a variety of properties. For example, if you wanted to learn which countries have the most Apache servers then you would use facets. If you wanted to figure out which version of nginx is most popular, you would use facets. Or if you wanted to see what the uptime distribution is for Microsoft-IIS servers then you would use facets.

The following script shows how to use the shodan.Shodan.count() method to search Shodan without returning any results as well as asking the API to return faceted information on the organization, domain, port, ASN and country.

#!/usr/bin/env python
# Search Shodan and print summary information for the query.
# Author: achillean
import shodan
import sys
# Configuration
# The list of properties we want summary information on

# We only care about the top 3 countries, this is how we let Shodan know to return 3 instead of the
# default 5 for a facet. If you want to see more than 5, you could do ('country', 1000) for example
# to see the top 1,000 countries for a search query.
('country', 3), ] FACET_TITLES = {
'org': 'Top 5 Organizations',
'domain': 'Top 5 Domains',
'port': 'Top 5 Ports',
'asn': 'Top 5 Autonomous Systems',
'country': 'Top 3 Countries', } # Input validation if len(sys.argv) == 1:
print('Usage: %s <search query>' % sys.argv[0])
sys.exit(1) try:
# Setup the api
api = shodan.Shodan(API_KEY)
# Generate a query string out of the command-line arguments
query = ' '.join(sys.argv[1:])
# Use the count() method because it doesn't return results and doesn't require a paid API plan
# And it also runs faster than doing a search().
result = api.count(query, facets=FACETS)
print('Shodan Summary Information')
print('Query: %s' % query)
print('Total Results: %s\n' % result['total'])
# Print the summary info from the facets
for facet in result['facets']:
for term in result['facets'][facet]:
print('%s: %s' % (term['value'], term['count']))
# Print an empty line between summary info
print('') except Exception as e:
print('Error: %s' % e)
sys.exit(1) """ Sample Output ============= ./ apache Shodan Summary Information Query: apache Total Results: 34612043 Top 5 Organizations 808061 Ecommerce Corporation: 788704 Verio Web Hosting: 760112 Unified Layer: 627827, LLC: 567004 Top 5 Domains 562047 494399 385792 194817 151925 Top 5 Ports 80: 24118703 443: 8330932 8080: 1479050 81: 359025 8443: 231441 Top 5 Autonomous Systems as32392: 580002 as2914: 465786 as26496: 414998 as48030: 332000 as8560: 255774 Top 3 Countries US: 13227366 DE: 2900530 JP: 2014506 """

Access SSL certificates in Real-Time

The new Shodan Streaming API provides real-time access to the information that Shodan is gathering at the moment. Using the Streaming API, you get the raw access to potentially all the data that ends up in the Shodan search engine. Note that you can’t search with the Streaming API or perform any other operations that you’re accustomed to with the REST API. This is meant for large-scale consumption of real-time data.

This script only works with people that have a subscription API plan! And by default the Streaming API only returns 1% of the data that Shodan gathers. If you wish to have more access please contact us at for pricing information.

#!/usr/bin/env python
# Stream the SSL certificates that Shodan is collecting at the moment
# WARNING: This script only works with people that have a subscription API plan!
# And by default the Streaming API only returns 1% of the data that Shodan gathers.
# If you wish to have more access please contact us at for pricing
# information.
# Author: achillean
import shodan
import sys
# Configuration

# Setup the api
api = shodan.Shodan(API_KEY)
print('Listening for certs...')
for banner in[443, 8443]):
if 'ssl' in banner:
# Print out all the SSL information that Shodan has collected
print(banner['ssl']) except Exception as e:
print('Error: {}'.format(e))

GIF Creator

Shodan keeps a full history of all the information that has been gathered on an IP address. With the API, you’re able to retrieve that history and we’re going to use that to create a tool that outputs GIFs made of the screenshots that the Shodan crawlers gather.

The below code requires the following Python packages:

  • arrow
  • shodan

The arrow package is used to parse the timestamp field of the banner into a Python datetime object.

In addition to the above Python packages, you also need to have the ImageMagick software installed. If you’re working on Ubuntu or another distro using apt you can run the following command:

sudo apt-get install imagemagick

This will provide us with the convert command which is needed to merge several images into an animated GIF.

There are a few key Shodan methods/ parameters that make the script work:

shodan.helpers.iterate_files() to loop through the Shodan data file
history flag on the method to get all the banners for an IP that Shodan has collected over the years

#!/usr/bin/env python
# Dependencies:
# - arrow
# - shodan
# Installation:
# sudo easy_install arrow shodan
# sudo apt-get install imagemagick
# Usage:
# 1. Download a json.gz file using the website or the Shodan command-line tool (
#    For example:
#        shodan download screenshots.json.gz has_screenshot:true
# 2. Run the tool on the file:
#        python screenshots.json.gz
import arrow
import os
import shodan
import shodan.helpers as helpers
import sys
# Settings
API_KEY = ''
MIN_SCREENS = 5 # Number of screenshots that Shodan needs to have in order to make a GIF
if len(sys.argv) != 2:

print('Usage: {} <shodan-data.json.gz>'.format(sys.argv[0]))
sys.exit(1) # GIFs are stored in the local "data" directory os.mkdir('data') # We need to connect to the API to lookup the historical host information api = shodan.Shodan(API_KEY) # Use the shodan.helpers.iterate_files() method to loop over the Shodan data file for result in helpers.iterate_files(sys.argv[1]):
# Get the historic info
host =['ip_str'], history=True)
# Count how many screenshots this host has
screenshots = []
for banner in host['data']:
# Extract the image from the banner data
if 'opts' in banner and 'screenshot' in banner['opts']:
# Sort the images by the time they were collected so the GIF will loop
# based on the local time regardless of which day the banner was taken.
timestamp = arrow.get(banner['timestamp']).time()
sort_key = timestamp.hour
# Ignore any further screenshots if we already have MAX_SCREENS number of images
if len(screenshots) >= MAX_SCREENS:
# Extract the screenshots and turn them into a GIF if we've got the necessary
# amount of images.
if len(screenshots) >= MIN_SCREENS:
for (i, screenshot) in enumerate(sorted(screenshots, key=lambda x: x[0], reverse=True)):
open('/tmp/gif-image-{}.jpg'.format(i), 'w').write(screenshot[1].decode('base64'))
# Create the actual GIF using the ImageMagick "convert" command
os.system('convert -layers OptimizePlus -delay 5x10 /tmp/gif-image-*.jpg -loop 0 +dither -colors 256 -depth 8 data/{}.gif'.format(result['ip_str']))
# Clean up the temporary files
os.system('rm -f /tmp/gif-image-*.jpg')
# Show a progress indicator

The full code is also available on GitHub:







2024, achillean

March 14, 2024 1.0