Measuring request timing with curl shows whether delay comes from name resolution, connection setup, TLS negotiation, or the server's first-byte response. That split is useful when an API call feels slow, a health check starts missing latency targets, or two environments behave differently even though they return the same content.

curl exposes transfer timing fields through --write-out, printing values such as {time_namelookup}, {time_connect}, {time_appconnect}, {time_starttransfer}, and {time_total} after the request completes. Those values all start at the beginning of the transfer, so they show how much time is spent in DNS resolution, TCP setup, TLS handshake, server processing until the first byte, and the full transfer from start to finish.

These measurements come from the client side, so DNS cache state, proxies, redirect behavior, connection quality, and temporary congestion can shift the numbers between runs. When timing redirecting URLs, add --location so the final destination is measured consistently, and compare repeated samples instead of reacting to a single isolated result. The sample endpoints below stay masked while preserving the shape of a normal API health check and a slower first-byte response.

Steps to measure request timing with cURL:

  1. Run one request with the response body discarded and print only the total duration in seconds.
    $ curl --silent --show-error --output /dev/null --write-out "time_total=%{time_total}\n" https://api.example.net/health
    time_total=0.184672

    time_total is the full elapsed time observed by curl from the start of the request until the transfer completes.

  2. Print the main timing phases for an HTTPS request to see where delay is accumulating.
    $ curl --silent --show-error --output /dev/null --write-out "dns=%{time_namelookup}\nconnect=%{time_connect}\ntls=%{time_appconnect}\nttfb=%{time_starttransfer}\ntotal=%{time_total}\n" https://api.example.net/health
    dns=0.004318
    connect=0.029441
    tls=0.081972
    ttfb=0.176548
    total=0.176904

    time_starttransfer includes everything up to the first response byte, while time_total adds the remaining download time. For HTTPS requests, time_appconnect shows the TLS handshake portion of the transfer.

  3. Repeat the same request several times before drawing conclusions from a latency spike.
    $ for i in $(seq 1 3); do curl --silent --show-error --output /dev/null --write-out "%{time_total}\n" https://api.example.net/health; done
    0.181227
    0.193904
    0.172611

    Small variation between runs is normal because network path, remote load, and local cache state change continuously. Look for a stable range or repeated outliers instead of one best-case number.

  4. Compare a normal endpoint and a deliberately delayed endpoint with the same timing format.
    $ for url in https://api.example.net/health https://api.example.net/health?profile=slow-ttfb; do curl --silent --show-error --output /dev/null --write-out "url=%{url_effective} ttfb=%{time_starttransfer} total=%{time_total}\n" "$url"; done
    url=https://api.example.net/health ttfb=0.178402 total=0.178691
    url=https://api.example.net/health?profile=slow-ttfb ttfb=1.427561 total=1.427856

    A larger jump in time_starttransfer usually points to server-side delay before the first byte, while a wider gap between time_starttransfer and time_total points to response-body transfer time.