DEV Community

Alex Spinov
Alex Spinov

Posted on

Shodan Has a Free API — Search Every Device Connected to the Internet

Shodan is the search engine for Internet-connected devices. Servers, webcams, industrial systems, databases — if it's online, Shodan has indexed it.

Their API gives you programmatic access to this data. The free tier includes search, host lookup, and DNS resolution.

Why Security Pros Use Shodan

A penetration tester was hired to assess a company's external attack surface. Instead of running port scans (which would take hours and trigger alerts), they queried Shodan. In seconds, they had a complete map of every public-facing service — including an exposed MongoDB on port 27017 with no authentication.

The vulnerability had been live for 6 months. Nobody knew.

Get Your Free API Key

  1. Create account at shodan.io
  2. Your API key is on your account page
  3. Free tier: search filters limited, but host lookup is unlimited

Look Up Any IP

\`python
import requests

API_KEY = "your-shodan-api-key"

def lookup_host(ip):
"""Get all known services running on an IP."""

response = requests.get(
    f"https://api.shodan.io/shodan/host/{ip}?key={API_KEY}"
)

if response.status_code == 200:
    data = response.json()

    print(f"IP: {data['ip_str']}")
    print(f"Organization: {data.get('org', 'Unknown')}")
    print(f"OS: {data.get('os', 'Unknown')}")
    print(f"Country: {data.get('country_name', 'Unknown')}")
    print(f"Open ports: {data.get('ports', [])}")

    for service in data.get("data", []):
        port = service["port"]
        product = service.get("product", "Unknown")
        version = service.get("version", "")
        print(f"  Port {port}: {product} {version}")

        # Check for vulnerabilities
        vulns = service.get("vulns", {})
        if vulns:
            print(f"  ⚠ Vulnerabilities: {', '.join(list(vulns.keys())[:5])}")
Enter fullscreen mode Exit fullscreen mode

lookup_host("8.8.8.8")
`\

Search for Exposed Services

\`python
def search_shodan(query, max_results=5):
"""Search Shodan for devices matching a query."""

response = requests.get(
    f"https://api.shodan.io/shodan/host/search?key={API_KEY}&query={query}"
)

if response.status_code == 200:
    data = response.json()
    print(f"Total results: {data['total']}")

    for match in data["matches"][:max_results]:
        ip = match["ip_str"]
        port = match["port"]
        org = match.get("org", "Unknown")
        country = match.get("location", {}).get("country_name", "Unknown")

        print(f"\n{ip}:{port} | {org} | {country}")

        # Show banner snippet
        banner = match.get("data", "")[:100]
        if banner:
            print(f"  Banner: {banner}")
Enter fullscreen mode Exit fullscreen mode

Find exposed Elasticsearch instances

search_shodan("product:elastic port:9200")
`\

DNS Resolution

Resolve domains to IPs (free, no query credits):

\`python
def resolve_domains(domains):
"""Resolve domain names to IP addresses."""

domain_str = ",".join(domains)
response = requests.get(
    f"https://api.shodan.io/dns/resolve?hostnames={domain_str}&key={API_KEY}"
)

for domain, ip in response.json().items():
    print(f"{domain} → {ip}")
    if ip:
        # Now look up the IP for full details
        lookup_host(ip)
Enter fullscreen mode Exit fullscreen mode

resolve_domains(["google.com", "github.com", "cloudflare.com"])
`\

Monitor Your Own Infrastructure

\`python
def audit_my_servers(ips):
"""Security audit of your own servers."""

issues = []

for ip in ips:
response = requests.get(
f"https://api.shodan.io/shodan/host/{ip}?key={API_KEY}"
)

if response.status_code != 200:
    continue

data = response.json()

# Check for common issues
for service in data.get("data", []):
    port = service["port"]
    product = service.get("product", "")

    # Exposed databases
    if port in [27017, 6379, 9200, 5432, 3306]:
        issues.append(f"⚠ {ip}:{port} - Database exposed ({product})")

    # Unencrypted services
    if port in [21, 23, 80] and service.get("ssl") is None:
        issues.append(f"⚠ {ip}:{port} - Unencrypted service ({product})")

    # Known vulnerabilities
    if service.get("vulns"):
        vuln_count = len(service["vulns"])
        issues.append(f"🔴 {ip}:{port} - {vuln_count} known CVEs")
Enter fullscreen mode Exit fullscreen mode

if issues:
print("SECURITY ISSUES FOUND:")
for issue in issues:
print(f" {issue}")
else:
print("✓ No obvious issues found")

Enter fullscreen mode Exit fullscreen mode




Audit your own servers (ONLY scan IPs you own!)

audit_my_servers(["your.server.ip.here"])
`\

Useful Search Queries

Query What it finds
product:mongodb\ MongoDB instances
port:22 country:US\ SSH servers in the US
http.title:"Dashboard"\ Web dashboards
ssl.cert.issuer.cn:*.gov\ Government SSL certs
vuln:CVE-2021-44228\ Log4Shell vulnerable
has_screenshot:true\ Devices with screenshots

Free Tier Limits

Feature Free Membership ($59)
Search queries Limited filters All filters
Host lookups Unlimited Unlimited
Scan credits 100/month Unlimited
Results per query 100 Unlimited
Network monitoring No Yes

Important: Ethics and Legality

Shodan indexes publicly available data. However:

  • Only scan/probe systems you own or have permission to test
  • Looking up data in Shodan is generally legal (it's public info)
  • Acting on that data (accessing exposed databases) without permission is illegal
  • Use this for defensive security — protecting your own infrastructure

I write about free APIs for developers and security professionals. More tutorials on my GitHub.

Top comments (0)