Output Formats¶
httptap supports multiple output formats to suit different use cases, from interactive troubleshooting to automated scripting.
Rich Mode (Default)¶
The default output format uses the Rich library to display a beautiful waterfall table in your terminal.
Features¶
- Colorized output with syntax highlighting
- Visual progress bars for timing phases
- Structured tables for easy reading
- Network details including IP, TLS version, and certificate info
- Response metadata showing status, body size, the
Serverheader, and the redirect target
When to Use¶
- Interactive debugging sessions
- Visual inspection of request performance
- Presentation of timing data to stakeholders
Compact Mode¶
One human-readable line per step, designed for terminal logs and redirect-chain tracing.
Example Output¶
Step 1: 200 GET https://httpbin.io/get | dns=8.9ms connect=97.0ms tls=194.6ms ttfb=446.0ms total=447.3ms | 389 B
Features¶
- Single line per step — HTTP status first, then method and URL, then per-phase timings, then human-readable body size.
- Timings carry
mssuffix so they read naturally alongside prose log entries. - Response size is formatted with the appropriate unit (
B,KB,MB). - Redirect summary table is still printed after the per-step lines so the overall shape of the chain stays visible.
When to Use¶
- Append to log files
- Quick performance comparisons
- CI / CD pipeline output where you still want to see the URL and status
- Terminal-friendly summaries when the full waterfall is too noisy
Metrics-Only Mode¶
Raw metrics without formatting, optimized for parsing by other tools.
Example Output¶
Step 1: dns=30.1 connect=97.3 tls=199.0 ttfb=472.2 total=476.0 status=200 bytes=389 ip=44.211.11.205 family=IPv4 tls_version=TLSv1.2 proxy=direct
Features¶
- Machine-parseable format
- Complete metrics including network details
- Consistent structure for easy extraction
- No colors or formatting characters
- Escaped values use percent-encoding when needed so every metric remains a single
key=valuetoken.
When to Use¶
- Scripting and automation
- Data collection for analysis
- Integration with monitoring tools
- Parsing with awk/grep/sed
Combined with --compact¶
--metrics-only takes precedence over --compact. Passing both flags still emits the machine-readable key=value line (so existing scripts keep working) and prints a warning to stderr that --compact was ignored.
Parsing Examples¶
# Extract TTFB values
httptap --metrics-only https://httpbin.io/delay/1 | grep -oP 'ttfb=\K[0-9.]+'
# Get all timing metrics
httptap --metrics-only https://httpbin.io/get | \
awk '{for(i=1;i<=NF;i++){if($i ~ /=/) print $i}}'
JSON Export¶
Full request data exported as structured JSON for comprehensive analysis.
Pass - as the path to write the JSON to stdout (the regular report is suppressed); status messages go to stderr:
JSON Structure¶
{
"schema_version": 1,
"httptap_version": "0.6.3",
"timestamp": "2026-09-18T08:00:00Z",
"initial_url": "https://httpbin.io",
"total_steps": 1,
"steps": [
{
"url": "https://httpbin.io",
"step_number": 1,
"request": {
"method": "GET",
"headers": {},
"body_bytes": 0
},
"timing": {
"dns_ms": 8.947,
"connect_ms": 96.977,
"tls_ms": 194.566,
"ttfb_ms": 445.951,
"total_ms": 447.344,
"wait_ms": 145.461,
"xfer_ms": 1.392,
"is_estimated": false
},
"network": {
"ip": "44.211.11.205",
"ip_family": "IPv4",
"http_version": "HTTP/2.0",
"tls_version": "TLSv1.2",
"tls_cipher": "ECDHE-RSA-AES128-GCM-SHA256",
"cert_cn": "httpbin.io",
"cert_days_left": 41,
"cert_sans": ["httpbin.io", "*.httpbin.io"],
"cert_issuer": "WE1",
"cert_serial": "05BB0F0AA84C8FECE0E72D805BA7A5D2B",
"cert_not_before": "2026-08-01T00:00:00+00:00",
"cert_not_after": "2026-10-30T00:00:00+00:00",
"tls_verified": true,
"tls_custom_ca": false,
"proxy_url": null,
"proxy_source": null
},
"response": {
"status": 200,
"bytes": 389,
"content_type": "application/json",
"server": "gunicorn/19.9.0",
"date": "2026-09-18T07:59:59+00:00",
"location": null,
"headers": {
"date": "Fri, 18 Sep 2026 07:59:59 GMT",
"content-type": "application/json",
"server": "gunicorn/19.9.0"
}
},
"error": null,
"note": null,
"redirect_limit_reached": false,
"proxy": null
}
],
"summary": {
"total_time_ms": 447.344,
"final_status": 200,
"final_url": "https://httpbin.io",
"final_bytes": 389,
"errors": 0
}
}
Field Reference¶
The top-level metadata identifies the report format and when it was produced. Consumers should use schema_version to select compatible parsing logic.
| Field | Type | Description |
|---|---|---|
schema_version | integer | Version of the JSON report format. The current version is 1. |
httptap_version | string | Version of httptap that generated the report. |
timestamp | string | Export creation time in RFC 3339 UTC format, for example 2026-09-18T08:00:00Z. |
initial_url | string | URL passed to httptap before redirects. |
total_steps | integer | Number of entries in steps. |
steps | array | Per-request measurements, including each followed redirect. |
summary | object | Aggregate values for the export. |
Timing values ending in _ms are milliseconds. Request and response body sizes are bytes. Response sizes (bytes, final_bytes) count the body as received on the wire, before Content-Encoding decoding, like curl's size_download. Certificate and response dates are ISO 8601/RFC 3339 timestamps when available. See the example above for the nested steps and summary structure.
Credentials are masked: the password (or a bare token) in the userinfo of step urls, response.location, the Location and Content-Location headers, and network.proxy_url is replaced with ****, and sensitive headers such as Authorization and Set-Cookie are masked as well.
network.tls_custom_ca is true when --cacert was used and false otherwise. A step's redirect_limit_reached is true when --follow stopped at the 10-redirect limit on that step; such a step also counts toward summary.errors.
A step with an error keeps the response it had already received: if the status line and headers arrived before the failure (for example, the body stalled past -m/--max-time), response.status, response.headers and response.bytes (the body bytes received so far) are filled in. A step that failed before the status line has response.status: null. The exit code is still the network error code (75).
Features¶
- Complete data export of all phases
- Structured format for easy parsing
- Redirect chain support with multiple steps
- Metadata preservation (headers, timestamps)
- Error information when requests fail
When to Use¶
- Post-processing analysis
- Integration with data pipelines
- Long-term performance tracking
- Detailed debugging sessions
- Sharing results with team members
Processing Examples¶
Using jq to extract specific fields:
# Get total time
jq '.summary.total_time_ms' output.json
# Extract all TTFB values
jq '.steps[].timing.ttfb_ms' output.json
# Get certificate expiration
jq '.steps[0].network.cert_days_left' output.json
# Filter failed requests
jq 'select(.summary.errors > 0)' output.json
HAR Export¶
--har PATH writes the analysed request chain as an HTTP Archive (HAR) 1.2 document, the format that browser DevTools and HAR viewers read. Pass - to write it to stdout (the regular report is suppressed); status messages go to stderr:
httptap --follow --har run.har https://httpbin.io/redirect/2
httptap --har - https://httpbin.io/get | jq '.log.entries[].timings'
--har and --json can be combined, but only one of them can write to stdout. If the file cannot be written, httptap exits with code 73, as it does for --json.
To open the file in Chrome DevTools, open the Network panel and use Import HAR file (the upload arrow in the toolbar), or drag the file onto the panel. Firefox DevTools offers Import HAR in the Network panel's settings menu.
HAR Structure¶
A trimmed example for a single HTTPS request:
{
"log": {
"version": "1.2",
"creator": { "name": "httptap", "version": "0.7.0" },
"pages": [
{
"startedDateTime": "2026-10-06T08:00:00.000+00:00",
"id": "page_1",
"title": "https://httpbin.io/get",
"pageTimings": { "onContentLoad": -1, "onLoad": -1 }
}
],
"entries": [
{
"pageref": "page_1",
"startedDateTime": "2026-10-06T08:00:00.000+00:00",
"time": 448.2,
"request": {
"method": "GET",
"url": "https://httpbin.io/get",
"httpVersion": "HTTP/2.0",
"cookies": [],
"headers": [],
"queryString": [],
"headersSize": -1,
"bodySize": 0
},
"response": {
"status": 200,
"statusText": "OK",
"httpVersion": "HTTP/2.0",
"cookies": [],
"headers": [{ "name": "content-type", "value": "application/json; charset=utf-8" }],
"content": { "size": 389, "mimeType": "application/json; charset=utf-8" },
"redirectURL": "",
"headersSize": -1,
"bodySize": 389,
"_transferSize": 389
},
"cache": {},
"timings": {
"blocked": -1,
"dns": 8.9,
"connect": 291.6,
"send": 0,
"wait": 146.4,
"receive": 1.3,
"ssl": 194.6
},
"serverIPAddress": "44.211.11.205",
"_tls": {
"version": "TLSv1.2",
"cipher": "ECDHE-RSA-AES128-GCM-SHA256",
"certCN": "httpbin.io",
"certIssuer": "Amazon RSA 2048 M03",
"certDaysLeft": 200,
"verified": true
}
}
]
}
}
The document contains:
- One page for the run. Its
titleis the URL passed to httptap; the page load timings do not apply and are-1. - One entry per request, in redirect-chain order, each pointing to the page through
pageref. httptap measures durations rather than wall-clock start times, so the entries are laid out back to back and the last one ends when the file is written. - Request: method, URL, negotiated HTTP version, the headers httptap was given (
-Hand theContent-Typederived from--data), the parsed query string andbodySize. The request body itself is never written. - Response: status, HTTP version, headers,
redirectURL(theLocationheader),content.mimeType(theContent-Type, orx-unknownwhen absent) and the body size. httptap does not decode the body, socontent.sizeandbodySizeare the size as received on the wire, beforeContent-Encodingdecoding. The body text is never included. serverIPAddress: the address httptap connected to.
statusText is the standard reason phrase for the status code (empty for unknown codes), and headersSize is -1: httptap does not record the reason phrase or header sizes sent on the wire. The cookies arrays are empty; Cookie and Set-Cookie appear, masked, in the headers.
Timings¶
| HAR field | httptap value | Notes |
|---|---|---|
blocked | -1 | Not measured. |
dns | dns_ms | |
connect | connect_ms + tls_ms | Includes the TLS handshake, as the HAR specification requires. |
ssl | tls_ms | HTTPS only, -1 for plain HTTP. Already part of connect. |
send | 0 | Not measured separately; sending the request is part of wait. |
wait | wait_ms | Server processing, up to the first response byte. |
receive | xfer_ms | Body transfer. |
time is the sum of blocked, dns, connect, send, wait and receive, leaving out -1 values. ssl is not added again because connect already contains it. Values are milliseconds rounded to three decimals. When the connection and TLS phases were estimated rather than measured (is_estimated in the JSON export), timings carries "_estimated": true.
Failed Requests¶
A step that failed is still exported. Its response.status is 0, or the status that had already arrived (for example, when the body stalled past -m/--max-time), and the error message is in response._error, where Chrome DevTools reads it when importing a HAR. Because a failed step has no measured phases, its dns, connect and ssl are -1, send, wait and receive are 0, and time is 0.
Custom Fields¶
The HAR specification reserves fields starting with _ for extensions. httptap adds:
| Field | Location | Contents |
|---|---|---|
_tls | entry | version, cipher, certCN, certIssuer, certDaysLeft, verified; HTTPS only. |
_proxy | entry | url (credentials masked) and source of the proxy the request went through. |
_redirectLimitReached | entry | true when --follow stopped at the 10-redirect limit on this step. |
_estimated | timings | true when the connection and TLS timings were estimated. |
_transferSize | response | Body bytes received on the wire, -1 when no response arrived. |
_error, _errorKind | response | Error message and its kind (network or internal) for a failed step. |
Redaction¶
The HAR export applies the same masking as the JSON export: the password (or a bare token) in URL userinfo is replaced with **** in request URLs, the page title, redirectURL, the Location and Content-Location headers and the proxy URL, and sensitive headers such as Authorization, Cookie and Set-Cookie are masked.
Warning
Paths and query strings are kept, and HAR files are often attached to bug reports. Review a HAR file for sensitive data before sharing it.
Prometheus Textfile Export¶
Write a node_exporter textfile collector report with --prometheus PATH:
The file is written atomically. Every sample carries a host label (hostname only) and the redirect-chain step, so several probes can share one textfile directory. Exported gauges:
| Metric | Extra labels | Meaning |
|---|---|---|
httptap_request_success | 1 if the step completed, 0 on a network/TLS error | |
httptap_request_duration_seconds | phase | dns, connect, tls, ttfb, wait, xfer, total |
httptap_response_status_code | HTTP status of the step | |
httptap_response_body_size_bytes | Response body size on the wire | |
httptap_last_run_timestamp_seconds | Unix time the file was written (host only) |
Failed steps export only httptap_request_success 0, so an outage never looks like a fast response. Paths and query strings are never used as labels.
OpenTelemetry Export¶
--otlp ENDPOINT sends OTLP/HTTP traces. Install the optional extra first:
pip install 'httptap[otel]'
httptap --otlp http://localhost:4318/v1/traces https://api.example.com/health
A run is exported as one trace: an httptap.analysis root span with one http.request span per redirect step, laid out one after another, and child spans for the DNS, connection, TLS, server wait and transfer phases. Delivery is bounded by -m/--max-time; a collector error is reported as a warning and does not change the exit code. The export omits the full request URL, so query parameters are not sent to the collector.
Redirect Chains¶
When using --follow, all output formats include data for each step in the redirect chain.
Rich Mode¶
Shows a summary table with totals for the entire chain.
Compact Mode¶
Outputs one line per redirect step, followed by the redirect chain summary table.
Output:
Step 1: 302 GET https://httpbin.io/redirect/2 | dns=8.9ms connect=97.0ms tls=194.6ms ttfb=446.0ms total=447.3ms | 0 B
Step 2: 302 GET https://httpbin.io/relative-redirect/1 | dns=2.7ms connect=97.5ms tls=194.0ms ttfb=400.2ms total=400.6ms | 0 B
Step 3: 200 GET https://httpbin.io/get | dns=2.6ms connect=97.4ms tls=197.3ms ttfb=403.2ms total=404.0ms | 389 B
JSON Export¶
Includes all steps in the steps array with complete timing and metadata.
Combining Options¶
Output format options can be combined with other flags:
# Follow redirects with compact output
httptap --follow --compact https://httpbin.io/redirect/2
# Export redirect chain to JSON with metrics display
httptap --follow --json chain.json --metrics-only https://bit.ly/example
Note
When both --json and display modes (--compact, --metrics-only) are used together, the display mode shows on stdout while JSON is written to the file.
SLO Threshold Overlay¶
--slo KEY=MS[,KEY=MS...] augments every output mode with a pass/fail verdict evaluated against the final successful request.
- Rich mode — a framed panel is printed after the waterfall. The border is green for pass, red for fail, and each violation is listed with actual, threshold, and overrun in milliseconds.
- Compact mode — behaves as Rich mode above; the SLO panel is still printed after the one-line step summaries.
- Metrics-only — the line for the final successful step gains
slo=passorslo=fail slo_violations=<keys>tokens. Intermediate redirect steps remain unchanged. - JSON —
summary.slocontainspass,thresholds_ms, andviolations[](each withkey,threshold_ms,actual_ms,delta_ms). Absent when--slois not supplied.
A violation makes httptap exit with code 4 while still rendering the full output, so the evidence is preserved for post-mortem.
See the dedicated SLO Threshold Checking page for the specification grammar, evaluation rules, exit-code precedence, and CI / cron recipes.
What's Next?¶
-
Custom components, monitoring, batch analysis
-
Programmatic usage and extensions