How to check API response time

Run curl -s -o /dev/null -w '%{time_namelookup} %{time_connect} %{time_appconnect} %{time_starttransfer} %{time_total}\n' https://api.example.com/. Every number is seconds counted from the start of the request, so the gaps between them separate DNS, TCP, TLS and server think time. Take five samples, because one request is noise.

Why check this

A response time regression arrives as a ticket that says the app feels slow, with no layer named. Run this breakdown on staging before sign-off, and again after any infrastructure change: a new load balancer, a DNS provider swap, a certificate rotation. The five numbers tell you which team owns the regression. A 90 ms TLS handshake is not the API team's bug, and a time_starttransfer that sits 400 ms after time_appconnect is not the network team's bug either.

Prerequisites

Steps

  1. Step 1.

    Request the endpoint once and print the five timing marks.

    curl -s -o /dev/null -w 'dns %{time_namelookup}\ntcp %{time_connect}\ntls %{time_appconnect}\nttfb %{time_starttransfer}\ntotal %{time_total}\n' https://example.com/
    
    dns 0.012902
    tcp 0.044262
    tls 0.087263
    ttfb 0.123651
    total 0.123751

    Each value is cumulative. DNS took 13 ms, the TCP handshake added 31 ms, the TLS handshake added 43 ms, the server answered 36 ms later, and the body finished 0.1 ms after the first byte.

  2. Step 2.

    Repeat the request five times and read the spread, not the average.

    for i in 1 2 3 4 5; do curl -s -o /dev/null -w "$i  %{time_namelookup}  %{time_connect}  %{time_appconnect}  %{time_starttransfer}  %{time_total}\n" https://example.com/; done
    
    1  0.008890  0.050523  0.091733  0.125147  0.125283
    2  0.004757  0.033579  0.071862  0.104567  0.104729
    3  0.004729  0.033059  0.075750  0.113771  0.113894
    4  0.019789  0.051263  0.102031  0.143564  0.143694
    5  0.012504  0.041496  0.080000  0.111186  0.111280

    Total ran from 105 ms to 144 ms across the five, a spread of 39 ms on an endpoint that did nothing. Any single measurement you quote in a bug report has to survive that spread.

  3. Step 3.

    Ask for two resources in one command so the second reuses the open connection.

    curl -s -o /dev/null -o /dev/null -w '%{url}  dns %{time_namelookup}  tcp %{time_connect}  tls %{time_appconnect}  ttfb %{time_starttransfer}  total %{time_total}\n' https://example.com/ https://example.com/index.html
    
    https://example.com/  dns 0.010585  tcp 0.042180  tls 0.088957  ttfb 0.121083  total 0.121227
    https://example.com/index.html  dns 0.000000  tcp 0.000000  tls 0.000000  ttfb 0.031645  total 0.031762

    On the reused connection curl reports zero for DNS, TCP and TLS, and total falls from 121 ms to 32 ms. The 89 ms difference is setup cost that a real client pays once per connection, not once per call.

How to read the result

| What you see | What it means | What to do | | --- | --- | --- | | time_namelookup is the largest jump | The resolver is slow or the record is uncached | Measure the resolver on its own before touching the API | | time_connect minus time_namelookup is large | TCP setup is slow, so distance or packet loss | Compare from a second network, then look at the route | | time_appconnect minus time_connect is large | The TLS handshake is the cost | Check the certificate chain length and the session resumption setting | | time_starttransfer minus time_appconnect is large | The server is thinking | Hand the trace to the API team, the network is fine | | time_total minus time_starttransfer is large | The body is slow to stream | Check the payload size and whether compression is on |

Common mistakes

Sign: The number from curl is three times what the monitoring dashboard reports.Cause: curl opens a fresh connection for every invocation, so it pays DNS, TCP and TLS every time. A browser or a pooled HTTP client pays that once and reuses the socket. Step 3 shows the same endpoint at 121 ms cold and 32 ms warm.
Sign: One measurement looks fine, then the same command is slow an hour later.Cause: A single request carries the resolver cache state, the TLS session cache and whatever else the machine is doing. Five samples on the same endpoint varied by 39 ms with nothing changed. Quote the spread, not one figure.
Sign: time_total is close to zero and the numbers look excellent.Cause: The request was rejected before any work happened. A 401, a 404 or a cached 304 returns fast and proves nothing. Print %{http_code} and %{size_download} alongside the timings and confirm the response is the one under test.
Sign: The endpoint under test redirects, and the timings describe the wrong request.Cause: Without -L curl times the redirect, not the destination. With -L the timing variables describe the final hop only, while %{time_redirect} holds everything before it. Read both or the missing time is invisible.

What to check next

FAQ

How to check API latency without installing anything?

curl ships with Windows 10, macOS and most Linux images, so the command in step 1 runs as is. What varies between builds is protocol support, not the timing variables, which have been in curl since 7.9.

How to check API response time in Postman?

Postman shows one number next to the status code, which is the equivalent of time_total. It does not break the request into DNS, TCP and TLS, so a slow handshake and a slow handler look identical there. Use the curl breakdown to tell them apart.

What does curl time_total actually measure?

Seconds from the moment curl started the transfer to the moment the last byte of the body arrived, including redirects when -L is used. It excludes the time your shell spent starting curl.

Is an average of ten requests a better number?

An average hides the tail that users complain about. Report the slowest sample and the spread alongside the median. The five samples in step 2 differed by 39 ms with no change to the server.

How many samples are enough for a bug report?

Five from one location gives you a spread to quote. Anything about percentiles or sustained load needs a load tool, not curl in a loop, because a sequential loop never puts two requests on the server at once.

Verified

Verified by Maks Vernycurl 8.21.0

Each output block is what the command above it printed on that date, on the host named in the step. Figures read from a live site move between runs. Compare the shape of the answer rather than the digits, and see the methodology for how a page is re-verified.

basic5 minpublished updated Maks Verny