A lightweight, zero-dependency Python 3.10+ SDK for the Check-Host.cc API.
Distributed network diagnostics from 60+ global locations: ICMP ping, MTR, DNS,
HTTP, TCP, UDP, WHOIS and geolocation, with the same fluent surface as the
official nodejs-lib,
php-lib,
go-lib and
CheckHost4J — plus a few
extras specific to Python.
Full API reference: https://check-host.cc/docs.
- Zero runtime dependencies — built on top of
urllib.request. - Full Swagger 2.0.0 parity — every endpoint covered, including
/myinfo,/report/{uuid}/og-imageand/report/{uuid}/country-map. - Type hints throughout with a PEP 561
py.typedmarker. - POST-based requests — no URL-encoding pitfalls.
- Built-in polling helper
wait_for_report()so you don't have to babysit thereportendpoint by hand. - Automatic API-key injection from the constructor or the
CHECK_HOST_API_KEYenvironment variable. - Granular exception hierarchy — separate classes for 400, 404, 429 and 5xx.
- Client-side validation for ports, DNS record types, MTR repeats etc.
- OG-Image and country-map fetch + save helpers for status maps.
- Region / DNS-type / MTR-protocol constants for IDE autocompletion.
- Context-manager support (
with CheckHost() as ch: ...).
pip install check-host-apiRequires Python 3.10+. No further dependencies.
from checkhost import CheckHost
from checkhost.regions import Continent, DNSType
with CheckHost() as ch:
# Geolocation / ASN
info = ch.info("check-host.cc")
print(f"{info.ip} -> {info.city}, {info.country} ({info.asn.get('name')})")
# Ping check across Europe + North America, 3 packets per node
task = ch.ping(
"1.1.1.1",
region=[Continent.EUROPE, Continent.NORTH_AMERICA],
repeat_checks=3,
)
print(f"Task UUID: {task.uuid}")
# Block until every node reports (or 20s elapses)
report = ch.wait_for_report(task.uuid, max_wait=20.0)
print(f"{len(report.completed_nodes)} nodes reported")
# Save the dynamic status map (PNG, 1200x630) and country world map
ch.save_og_image(task.uuid, "./status.png")
ch.save_country_map(task.uuid, "./status.svg")The API works without a key (subject to public rate limits). For higher limits, provide an API key (UUID) via constructor or environment variable:
ch = CheckHost("YOUR_API_KEY_UUID")
# or
import os
os.environ["CHECK_HOST_API_KEY"] = "YOUR_API_KEY_UUID"
ch = CheckHost()When both are present, the constructor argument wins.
This SDK supports both minimal invocations and detailed, options-rich requests for every endpoint.
region: list of node names, ISO country codes, or continent codes (EU,NA,SA,AS,AF,OC). Continents cannot be mixed with specific node names.repeat_checks: number of repeated probes per node.0= single shot;>= 10enables Live Mode (continuous probing for ~repeat_checksseconds).timeout: per-request connection timeout in seconds (currently a no-op on the Check-Host backend, but accepted for forward compatibility).
Returns the requesting client's public IPv4 / IPv6 address.
ip = ch.myip()Geolocation + ASN + privacy / abuse / company data for the caller's IP.
Subject to bot-detection — repeated calls may yield a captcha URL in a
CheckHostRateLimitError.response.
info = ch.myinfo()
print(info.country_code, info.city, info.asn.get("name"))locations = ch.locations()
# locations["locationlist"] is a list of nodes, each with continent,
# countryCode, isp, locationname, sponsor, etc.info = ch.info("check-host.cc")record = ch.whois("check-host.cc")
# Returns the raw RDAP record (shape varies by registry).Each monitoring call returns a CheckCreated object with a uuid. Use
report(uuid) (single fetch) or wait_for_report(uuid) (polling
helper) to retrieve results.
# Minimal
task = ch.ping("8.8.8.8")
# With options
task = ch.ping(
"8.8.8.8",
region=["DE", "NL"],
repeat_checks=5, # >=10 enables Live Mode
timeout=5,
)from checkhost.regions import DNSType
# Minimal — defaults to A record
task = ch.dns("check-host.cc")
# Specific record type
task = ch.dns(
"check-host.cc",
query_method=DNSType.MX, # A / AAAA / MX / TXT / NS / CAA / ...
region=["US", "DE"],
)# Minimal — host + port
task = ch.tcp("1.1.1.1", 443)
# With options
task = ch.tcp(
"1.1.1.1",
80,
region=["DE", "NL"],
repeat_checks=3,
timeout=10,
)The Check-Host backend knows sensible default payloads for well-known
ports (DNS, NTP, SNMP, RIP, WireGuard, OpenVPN, Quake, Minecraft, …).
Pass a custom hex (or printable) payload only when probing unusual
services.
# Minimal — DNS query (default payload)
task = ch.udp("1.1.1.1", 53)
# Custom NTP payload to UDP/123
task = ch.udp(
"pool.ntp.org",
123,
payload="0b",
region=["EU"],
repeat_checks=2,
)# Minimal — full URL
task = ch.http("https://check-host.cc")
# With options
task = ch.http(
"https://check-host.cc/status",
region=["EU"],
repeat_checks=10, # Live Mode: 11 probes over ~10s
)from checkhost.regions import MTRProtocol, IPVersion
# Minimal
task = ch.mtr("1.1.1.1")
# Force TCP for ICMP-blocked paths
task = ch.mtr(
"1.1.1.1",
region=["EU"],
repeat_checks=10,
force_ip_version=IPVersion.V4,
force_protocol=MTRProtocol.TCP, # "udp" or "tcp"; default is ICMP
)report = ch.report(task.uuid)
print(report.is_complete, len(report.completed_nodes), report.pending_nodes)report = ch.wait_for_report(
task.uuid,
interval=1.5, # clamped to >=1.0 (API limit)
max_wait=30.0,
require_complete=True, # raise CheckHostTimeoutError on timeout
)png_bytes = ch.og_image(task.uuid)
# Or write straight to disk
ch.save_og_image(task.uuid, "./status.png")svg = ch.country_map(task.uuid) # default SVG
png_low = ch.country_map(task.uuid, format="png", resolution="low") # 800px
png_high = ch.country_map(task.uuid, format="png", resolution="high") # 2000px
# Convenience save
ch.save_country_map(task.uuid, "./status.svg")| Method | Endpoint | Returns |
|---|---|---|
ch.myip() |
GET /myip |
str |
ch.myinfo() |
GET /myinfo |
MinResponseINFO |
ch.locations() |
GET /locations |
dict[str, Any] |
ch.info(target) |
POST /info |
MinResponseINFO |
ch.whois(target) |
POST /whois |
dict[str, Any] |
ch.ping(target, *, region=None, repeat_checks=0, timeout=None) |
POST /ping |
CheckCreated |
ch.dns(target, *, query_method="A", region=None) |
POST /dns |
CheckCreated |
ch.tcp(target, port, *, region=None, repeat_checks=0, timeout=None) |
POST /tcp |
CheckCreated |
ch.udp(target, port, *, payload=None, region=None, repeat_checks=0, timeout=None) |
POST /udp |
CheckCreated |
ch.http(target, *, region=None, repeat_checks=0, timeout=None) |
POST /http |
CheckCreated |
ch.mtr(target, *, region=None, repeat_checks=10, force_ip_version=None, force_protocol=None) |
POST /mtr |
CheckCreated |
ch.report(uuid) |
GET /report/{uuid} |
Report |
ch.wait_for_report(uuid, *, interval=1.5, max_wait=30.0, require_complete=True) |
polls GET /report/{uuid} |
Report |
ch.og_image(uuid) |
GET /report/{uuid}/og-image |
bytes (PNG) |
ch.save_og_image(uuid, path) |
same | Path |
ch.country_map(uuid, *, format="svg", resolution="med") |
GET /report/{uuid}/country-map |
bytes |
ch.save_country_map(uuid, path, *, format="svg", resolution="med") |
same | Path |
The SDK rejects obviously bad input before issuing an HTTP call:
port: 1-65535repeat_checks(non-MTR): 0-120repeat_checks(MTR): 3-30query_method: one ofA,AAAA,MX,TXT,CAA,A/AAAA, … (full list incheckhost.regions.DNSType.ALL)force_ip_version: 4 or 6force_protocol:"icmp" | "udp" | "tcp"country_map.format:"svg" | "png"country_map.resolution:"low" | "med" | "high"
Use report.completed_nodes, report.pending_nodes and
report.is_complete to inspect progress; report.raw always preserves
the raw API payload, including any future fields the SDK doesn't yet
surface explicitly.
from checkhost.regions import Continent, DNSType, IPVersion, MTRProtocol
Continent.EUROPE # "EU"
Continent.ALL # ("EU", "NA", "SA", "AS", "AF", "OC")
DNSType.MX # "MX"
DNSType.A_AAAA # "A/AAAA" - Swagger 2.0 compound default
DNSType.ALL # ("A/AAAA", "A", "AAAA", "NS", ..., "DNSKEY")
IPVersion.V4 # 4
MTRProtocol.TCP # "tcp"from checkhost import (
CheckHost,
CheckHostBadRequestError,
CheckHostError,
CheckHostNetworkError,
CheckHostRateLimitError,
CheckHostTimeoutError,
CheckHostValidationError,
)
with CheckHost() as ch:
try:
task = ch.ping("1.1.1.1")
report = ch.wait_for_report(task.uuid, max_wait=15.0)
except CheckHostValidationError as exc:
# Invalid input - fix the call.
...
except CheckHostRateLimitError as exc:
# 429: provide an API key or back off.
...
except CheckHostBadRequestError as exc:
# 400: bad payload.
print(exc.status, exc.response)
except CheckHostTimeoutError:
# Polling deadline expired.
...
except CheckHostNetworkError:
# Connectivity / DNS / TLS issue.
...
except CheckHostError:
# Catch-all for the SDK.
...Hierarchy at a glance:
CheckHostError
+-- CheckHostNetworkError
+-- CheckHostTimeoutError
+-- CheckHostValidationError (also ValueError)
+-- CheckHostAPIError
+-- CheckHostBadRequestError (400)
+-- CheckHostNotFoundError (404)
+-- CheckHostRateLimitError (429)
+-- CheckHostServerError (5xx)
The SDK uses the standard logging module under the logger name
"checkhost". Enable debug logs to inspect every outgoing HTTP request:
import logging
logging.basicConfig(level=logging.DEBUG)
logging.getLogger("checkhost").setLevel(logging.DEBUG)urllib.request from the standard library is used, which means
HTTP_PROXY / HTTPS_PROXY / NO_PROXY environment variables are
honoured automatically and TLS verification follows the system trust
store.
git clone https://git.check-host.eu/Check-Host/python-lib
cd python-lib
pip install -e ".[dev]"
pytest # 155 unit tests, ~0.2s
ruff check . && mypy checkhostTo run the integration tests against the live API (consumes real
rate-limit budget — set CHECK_HOST_API_KEY for higher quotas):
pytest -m live # 8 live tests, ~6s with an API key@check-hostcc/check-host-api(Node.js — npm)check-hostcc/check-host-api-php(PHP — Composer)github.com/Check-Host/go-lib(Go)cc.checkhost:checkhost4j(Java)
Full API reference: https://check-host.cc/docs.