GET/api/v2/port
Port check API
Whether TCP ports on a host are open, closed or filtered, checked from Cloudflare's edge, with each open port's banner.
- 12 calls a minute per IP
- Same engine as the Port Checker tool
{
"query": {
"host": "github.com",
"ports": [
22,
80,
443
]
},
"host": "github.com",
"ip": "140.82.114.4",
"addresses": [
"140.82.114.4"
],
"network": {
"asn": 36459,
"name": "GitHub",
"prefix": "140.82.114.0/24",
"country": "US"
},
"cloudflare": false,
"checkedFrom": {
"colo": "HYD"
},
"summary": {
"open": 3,
"closed": 0,
"filtered": 0,
"unchecked": 0
},
"ports": [
{
"port": 22,
"service": "SSH",
"transport": "tcp",
"status": "open",
"reason": "connected",
"rttMs": 218,
"banner": "SSH-2.0-35b973d6",
"server": null,
"httpStatus": null,
"tlsVersion": null,
"risk": "medium"
},
{
"port": 80,
"service": "HTTP",
"transport": "tcp",
"status": "open",
"reason": "connected",
"rttMs": 204,
"banner": "HTTP 301 Moved Permanently",
"server": null,
"httpStatus": 301,
"tlsVersion": null,
"risk": null
},
{
"port": 443,
"service": "HTTPS",
"transport": "both",
"status": "open",
"reason": "connected",
"rttMs": 223,
"banner": "TLS",
"server": null,
"httpStatus": null,
"tlsVersion": null,
"risk": null
}
],
"issues": [
{
"severity": "info",
"code": "exposed_remote",
"message": "SSH (22) is open to the internet. Remote logins are scanned and attacked around the clock. Allow only your own addresses, or reach it through a VPN."
}
],
"tookMs": 1114
}How it works
Resolves the host once (every IPv4 address must be public), then opens a TCP connection to each port at its first IPv4 address from the Cloudflare data centre that took your request (checkedFrom.colo). An open web port is sent a HEAD (its Server header and status), a TLS port a ClientHello (the version it answers), and other services' first words are kept as banner.
open: the connection completed. closed: the address refused it after a real round trip. filtered: no answer within 4 seconds (a firewall drops it), never reported as closed. unchecked (with reason): the edge can't read it honestly: an address on Cloudflare's own network (cloudflare_network: Workers can't connect to Cloudflare's addresses, and its ports answer for Cloudflare, not the origin) or an answer indistinguishable from the runtime's own refusal (ambiguous). Open risky services (databases, remote desktops, Docker) come back in issues.
Port 25 can't be checked (Cloudflare blocks it outbound) and IPv6 targets can't be reached: both are a 422. A TCP check says nothing about UDP-only services (WireGuard, Minecraft Bedrock).
Query parameters
hoststringrequiredA hostname or public IPv4 address; a URL orhost:portis read as its host.portintegeroptionalOne TCP port, 1 to 65535. Sendportorports.portsstringoptionalUp to 10 ports, comma-separated; a range like8000-8003counts each port. Every 5 ports cost one unit of the limit.
Response fields
200 with this JSON object, plus tookMs, and incomplete with warnings when a source failed. Fields marked optional appear only when they apply.
queryobjectalwaysWhat was looked up, normalised (lower case, no trailing dot, IDNs as Punycode).hoststringalwaysThe host, without scheme, path or port; an internationalised name in its xn-- form.portsarray of integersalwaysThe ports checked, sorted.
hoststringalwaysThe host checked.ipstring or nullalwaysThe address every port was dialled at (the host's first IPv4 address); null when it has none.addressesarray of stringsalwaysEvery IPv4 address the name has.networkobject or nullalwaysThe network announcing the address (Team Cymru), or null.Show 4 fields
asnintegeralwaysAS number.namestringalwaysThe network's name.prefixstring or nullalwaysThe announced prefix.countrystring or nullalwaysThe block's registered country.
cloudflarebooleanalwaysWhether the address is on Cloudflare's network: its ports answer for Cloudflare's edge, and the edge can't dial them (unchecked).checkedFromobjectalwaysWhere the connections were made.colostring or nullalwaysThe Cloudflare data centre (IATA code), when known.
summaryobjectalwaysHow many ports in each state.Show 4 fields
openintegeralwaysOpen.closedintegeralwaysClosed.filteredintegeralwaysFiltered.uncheckedintegeralwaysUnchecked.
portsarray of objectsalwaysOne row per port, in the order ofquery.ports.Show 11 fields
portintegeralwaysThe port.servicestring or nullalwaysThe service usually on it ("HTTPS", "MySQL"), or null for a port we don't name.transportstring or nullalwaysHow that service talks:tcp,udporboth. A TCP check can't see a UDP service.one of
"tcp","udp","both",nullstatusstringalwaysopen,closed,filteredorunchecked.one of
"open","closed","filtered","unchecked"reasonstringalwaysThe evidence:connected,refused,timeout(no answer in 4 s), or why it wasn't checked:cloudflare_network,ambiguous,no_address.one of
"connected","refused","timeout","cloudflare_network","ambiguous","no_address"rttMsinteger or nullalwaysThe connection's round trip foropenandclosed, in milliseconds.bannerstring or nullalwaysWhat an open service said first (printable ASCII, at most 160 characters):nginx · HTTP 301 Moved Permanently,TLS 1.3,SSH-2.0-OpenSSH_9.6.serverstring or nullalwaysA web port'sServerheader.httpStatusinteger or nullalwaysA web port's HTTP status to ourHEAD /.tlsVersionstring or nullalwaysA TLS port's negotiated version (TLS 1.3,TLS 1.2).riskstring or nullalwaysHow risky the service is when open to everyone:high(attacked around the clock),medium(restrict it), null.one of
"high","medium",null
issuesarray of objectsalwaysProblems found in the target's configuration, worst first. Empty when there are none.severitystringalwayserrorbreaks something,warningrisks it,infois worth knowing.one of
"error","warning","info"codestringalwaysStable machine-readable code.messagestringalwaysThe problem and what to do about it, in plain English.
Errors
422target_not_allowedThe host is (or resolves to) a private or reserved address, has only IPv6 addresses, or a requested port is 25.503unavailableDNS didn't answer for the host.
Plus the errors every endpoint can return: 400 for a missing or unreadable parameter, 429 past the limit, 503 when every source failed.
Code samples
curl -G "https://dnsrobot.net/api/v2/port" \
--data-urlencode "host=github.com" \
--data-urlencode "ports=22,80,443"const params = new URLSearchParams({ host: "github.com", ports: "22,80,443" });
const res = await fetch(`https://dnsrobot.net/api/v2/port?${params}`);
const data = await res.json();
if (!res.ok) throw new Error(data.error.message);
console.log(data.ports);import requests
r = requests.get(
"https://dnsrobot.net/api/v2/port",
params={"host": "github.com", "ports": "22,80,443"},
timeout=30,
)
data = r.json()
if not r.ok:
raise RuntimeError(data["error"]["message"])
print(data["ports"])package main
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
)
func main() {
q := url.Values{"host": {"github.com"}, "ports": {"22,80,443"}}
res, err := http.Get("https://dnsrobot.net/api/v2/port?" + q.Encode())
if err != nil {
panic(err)
}
defer res.Body.Close()
var data map[string]any
if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
panic(err)
}
if res.StatusCode != http.StatusOK {
panic(data["error"])
}
fmt.Println(data["ports"])
}<?php
$query = http_build_query(["host" => "github.com", "ports" => "22,80,443"]);
$context = stream_context_create(["http" => ["ignore_errors" => true, "timeout" => 30]]);
$data = json_decode(file_get_contents("https://dnsrobot.net/api/v2/port?" . $query, false, $context), true);
if (isset($data["error"])) {
throw new RuntimeException($data["error"]["message"]);
}
print_r($data["ports"]);require "net/http"
require "json"
uri = URI("https://dnsrobot.net/api/v2/port")
uri.query = URI.encode_www_form(host: "github.com", ports: "22,80,443")
data = JSON.parse(Net::HTTP.get(uri))
raise data["error"]["message"] if data["error"]
puts data["ports"]// Cargo.toml: reqwest = { version = "0.13", features = ["blocking", "json"] }, serde_json = "1"
fn main() -> Result<(), Box<dyn std::error::Error>> {
let res = reqwest::blocking::get("https://dnsrobot.net/api/v2/port?host=github.com&ports=22%2C80%2C443")?;
let ok = res.status().is_success();
let data: serde_json::Value = res.json()?;
if !ok {
return Err(data["error"]["message"].to_string().into());
}
println!("{}", data["ports"]);
Ok(())
}using System.Text.Json;
using var http = new HttpClient();
var res = await http.GetAsync("https://dnsrobot.net/api/v2/port?host=github.com&ports=22%2C80%2C443");
var data = JsonDocument.Parse(await res.Content.ReadAsStringAsync()).RootElement;
if (!res.IsSuccessStatusCode)
throw new Exception(data.GetProperty("error").GetProperty("message").GetString());
Console.WriteLine(data.GetProperty("ports"));SSH, HTTP and HTTPS on github.com
GET /api/v2/port?host=github.com&ports=22%2C80%2C443{
"query": {
"host": "github.com",
"ports": [
22,
80,
443
]
},
"host": "github.com",
"ip": "140.82.114.4",
"addresses": [
"140.82.114.4"
],
"network": {
"asn": 36459,
"name": "GitHub",
"prefix": "140.82.114.0/24",
"country": "US"
},
"cloudflare": false,
"checkedFrom": {
"colo": "HYD"
},
"summary": {
"open": 3,
"closed": 0,
"filtered": 0,
"unchecked": 0
},
"ports": [
{
"port": 22,
"service": "SSH",
"transport": "tcp",
"status": "open",
"reason": "connected",
"rttMs": 218,
"banner": "SSH-2.0-35b973d6",
"server": null,
"httpStatus": null,
"tlsVersion": null,
"risk": "medium"
},
{
"port": 80,
"service": "HTTP",
"transport": "tcp",
"status": "open",
"reason": "connected",
"rttMs": 204,
"banner": "HTTP 301 Moved Permanently",
"server": null,
"httpStatus": 301,
"tlsVersion": null,
"risk": null
},
{
"port": 443,
"service": "HTTPS",
"transport": "both",
"status": "open",
"reason": "connected",
"rttMs": 223,
"banner": "TLS",
"server": null,
"httpStatus": null,
"tlsVersion": null,
"risk": null
}
],
"issues": [
{
"severity": "info",
"code": "exposed_remote",
"message": "SSH (22) is open to the internet. Remote logins are scanned and attacked around the clock. Allow…"
}
],
"tookMs": 1114
}Shortened for the page: long lists show their first items.
Open and closed ports on Nmap's test host
GET /api/v2/port?host=scanme.nmap.org&ports=22%2C80%2C3306{
"query": {
"host": "scanme.nmap.org",
"ports": [
22,
80,
3306
]
},
"host": "scanme.nmap.org",
"ip": "45.33.32.156",
"addresses": [
"45.33.32.156"
],
"network": {
"asn": 63949,
"name": "Akamai Connected Cloud",
"prefix": "45.33.32.0/19",
"country": "US"
},
"cloudflare": false,
"checkedFrom": {
"colo": "HYD"
},
"summary": {
"open": 2,
"closed": 1,
"filtered": 0,
"unchecked": 0
},
"ports": [
{
"port": 22,
"service": "SSH",
"transport": "tcp",
"status": "open",
"reason": "connected",
"rttMs": 223,
"banner": null,
"server": null,
"httpStatus": null,
"tlsVersion": null,
"risk": "medium"
},
{
"port": 80,
"service": "HTTP",
"transport": "tcp",
"status": "open",
"reason": "connected",
"rttMs": 247,
"banner": "Apache/2.4.7 (Ubuntu) · HTTP 200 OK",
"server": "Apache/2.4.7 (Ubuntu)",
"httpStatus": 200,
"tlsVersion": null,
"risk": null
},
{
"port": 3306,
"service": "MySQL",
"transport": "tcp",
"status": "closed",
"reason": "refused",
"rttMs": 232,
"banner": null,
"server": null,
"httpStatus": null,
"tlsVersion": null,
"risk": "high"
}
],
"issues": [
{
"severity": "info",
"code": "exposed_remote",
"message": "SSH (22) is open to the internet. Remote logins are scanned and attacked around the clock. Allow…"
}
],
"tookMs": 1123
}Shortened for the page: long lists show their first items.
An address on Cloudflare's network
GET /api/v2/port?host=1.1.1.1&port=443{
"query": {
"host": "1.1.1.1",
"ports": [
443
]
},
"host": "1.1.1.1",
"ip": "1.1.1.1",
"addresses": [
"1.1.1.1"
],
"network": {
"asn": 13335,
"name": "Cloudflare",
"prefix": "1.1.1.0/24",
"country": "AU"
},
"cloudflare": true,
"checkedFrom": {
"colo": "HYD"
},
"summary": {
"open": 0,
"closed": 0,
"filtered": 0,
"unchecked": 1
},
"ports": [
{
"port": 443,
"service": "HTTPS",
"transport": "both",
"status": "unchecked",
"reason": "cloudflare_network",
"rttMs": null,
"banner": null,
"server": null,
"httpStatus": null,
"tlsVersion": null,
"risk": null
}
],
"issues": [
{
"severity": "info",
"code": "cloudflare_network",
"message": "1.1.1.1 is on Cloudflare's network: its ports answer for Cloudflare's edge, not the server behin…"
}
],
"tookMs": 84
}Shortened for the page: long lists show their first items.