Documentation
Complete guide to setting up website monitoring with PingZen. API documentation, code examples, and best practices.
PingZen monitors report latency for PING and HTTP/HTTPS checks. Below is exactly how we measure, what each metric means, and how our numbers compare to industry-standard tools (Linux ping, curl, icmplib, fping).
PING (ICMP) — Kernel-Accurate RTT
For Ping monitors we shell out to the system ping binary (same as Datadog and most probes) and parse its statistics line. All reported RTT values come directly from the kernel — not from any Python timestamp — so they are identical to what you would see running ping on the probe host yourself.
How we measure (step by step)
- Run
ping -c N -i 0.2 <host>as an asyncio subprocess (N = configurable packet count, default 4). - iputils
pingusesgettimeofday()for send-side timestamp + kernelSO_TIMESTAMPcontrol message (cmsg) for receive-side — see iputils source. - Parse the
rtt min/avg/max/mdevline from stdout (Linux) orround-trip min/avg/max/stddev(macOS). - Store min_rtt, avg_rtt, max_rtt, jitter (mdev), and packet_loss as floats in the
check_resultstable. - The integer
response_time_mscolumn is set toround(avg_rtt)with a 1 ms floor — sub-ms RTTs round to 1 ms instead of 0, which reads as “no response”. - Subprocess fork+exec overhead (~10 ms wall-clock) is discarded — it never enters the RTT figures.
Stored fields
ping_avg_rtt
Average RTT in milliseconds (float, 3 decimals). Kernel-measured. Use this for power-user precision — e.g. sub-ms in-datacenter targets show as 0.260 ms.
ping_min_rtt / ping_max_rtt
Minimum and maximum RTT across all echo packets in one check (float).
ping_jitter
Mean deviation of RTTs from their average (mdev) as reported by Linux ping. Note: this is classical mdev, not the RFC 3550 inter-arrival jitter used by some RTP tools — the two formulas differ.
ping_packet_loss
Fraction of packets that did not receive a reply within timeout (0.0–100.0 %).
response_time_ms
Integer ms — equals round(ping_avg_rtt), floored at 1 ms. Primary UI metric; for sub-ms precision inspect ping_avg_rtt directly.
Sub-millisecond targets (in-datacenter)
If your probe is in the same datacenter as the target (typical for bare-metal setups), real RTT is 0.2–0.9 ms. The integer response_time_ms is floored at 1 ms to avoid displaying "0ms" (which reads as a failed check). The true sub-ms value is always preserved in the float ping_avg_rtt field — visible in check_results API and the chart tooltip.
HTTP / HTTPS — Time to First Byte with Breakdown
For HTTP/HTTPS monitors we use httpx.AsyncClient with the trace extension hook to capture individual phase timestamps. We record wall-clock total plus four sub-event durations. Convention matches curl time_total and time_starttransfer.
Per-check timing fields
response_time_ms (total)
Full wall-clock from before request-build until httpx await response returns. Matches curl time_total without body read. Includes TCP + TLS + TTFB plus ~5–15 ms of Python / httpx / asyncio overhead (pool lookup, response object construction, event-loop scheduling). This is the "what the client saw" figure.
timing_tcp_ms
TCP handshake duration: connection.connect_tcp.started → connection.connect_tcp.complete. Equivalent to curl time_connect - time_namelookup. One RTT under typical conditions.
timing_tls_ms
TLS handshake duration: connection.start_tls.started → connection.start_tls.complete. Equivalent to curl time_appconnect - time_connect. One RTT for TLS 1.3, ~2 RTTs for TLS 1.2.
timing_ttfb_ms
Request-send → first response byte: http11.send_request_headers.started → http11.receive_response_headers.started. Pure server processing + one RTT. Equivalent to curl time_starttransfer - time_pretransfer.
(not stored) DNS, body-read
DNS is usually cached by the httpx pool and not emitted as a separate trace event. Response body read is not included in total — we break before response.content. Add either as Server-Timing header custom metric if needed.
Why total > tcp + tls + ttfb
On warm connection-pool reuse the sub-events may not fire at all (connection already open) yet total still reports 5–20 ms. The difference is real Python / httpx / asyncio overhead: auth and header construction before the first trace event, response object build and close after the last event, plus event-loop scheduling under concurrent load. This is normal for all Python-based HTTP monitors — curl shows the same 2–8 ms gap between sum-of-phases and total. The separate timing_* fields give you the pure network / server picture.
Industry Standards We Follow
PingZen measurements are RFC-compliant and align with major tools:
- RFC 2681 — Round-trip Delay Metric for IPPM: our ICMP path via system
pingqualifies as a Type-P-Round-trip-Delay measurement. - Datadog Synthetic ICMP tests use
pingsubprocess + stdout parse — identical to PingZen. - Prometheus blackbox_exporter uses Go
time.Now()userspace timestamps, no SO_TIMESTAMPING — PingZen has the same precision class via kernel recv-side cmsg. - SmokePing / fping: batched C raw sockets for efficiency. Same metric semantics (min/avg/max/jitter/loss), different implementation.
- RIPE Atlas: ICMP echo with internal firmware timestamping. Precision not publicly documented.
- icmplib (Python): pure-Python raw socket. Validated against PingZen subprocess — RTT matches within 0.1 ms.
- SO_TIMESTAMPING kernel hardware timestamps exist for nanosecond precision but are overkill for application-level uptime monitoring (and unsupported in containerized environments).
Common Questions
My router shows sub-millisecond ping but PingZen shows 30+ ms. Why?
Most likely different targets on different paths. Run ping <target_ip> on the same host that runs the PingZen probe — you will almost always get the same number we report. If not, paste the shell output and we will investigate routing.
HTTP response_time is 24 ms but my server reports 6 ms processing. Where are the missing 18 ms?
Total is wall-clock from client init; your server-side 6 ms is pure processing. The delta includes TCP ACK propagation, httpx pool lookup, Python object construction, and asyncio scheduling. For pure server latency use timing_ttfb_ms (which already excludes TCP and TLS).
Can I export the raw float ping_avg_rtt?
Yes — the check_results API returns ping_avg_rtt as a float. UI charts tooltip shows it with 3 decimals. For sub-ms in-DC pings this is the value to watch.
Why is jitter (mdev) sometimes higher than expected?
We report Linux ping mdev (mean deviation of RTTs from their average), not RFC 3550 inter-arrival jitter used by some RTP tools. Both are valid, they measure different things. On stable in-DC links mdev is typically 0.01–0.5 ms.
Common Questions
What protocols can I monitor?
PingZen supports 23 protocols: HTTP/HTTPS, WebSocket (WS/WSS), TCP, UDP, ICMP Ping, gRPC, DNS, WHOIS, SSL certificates, Email (SMTP/IMAP/POP3), FTP/FTPS, DNSBL, PageSpeed, SOCKS5, MTProxy, API Check, and Transaction. You can monitor websites, APIs, servers, databases, and any network service.
How fast can I get alerts?
Telegram alerts are delivered within 1-2 seconds of detection. Slack and Discord notifications arrive almost instantly. You can configure multiple alert channels for redundancy.
Can I organize monitors by project?
Yes! PingZen supports workspaces, which let you organize monitors by project, environment, or team. Each workspace can have its own alert configurations and team members.
Is there an API for automation?
Absolutely. PingZen provides a full REST API with OpenAPI documentation. You can create, update, and delete monitors programmatically.
How do status pages work?
Status pages are public, branded pages showing your services' uptime. You can display real-time status and allow customers to subscribe for updates.
What happens if I reach my monitor limit?
We'll notify you when approaching your limit. You can pause some monitors or contact us for increased capacity. We never stop monitoring without warning, ensuring your critical services stay protected.
Ready to stop missing downtime?
Join thousands of teams who trust PingZen. Setup takes 30 seconds.