Python
server response time
requests library
POST request
performance testing

How to measure server response time for Python requests POST-request

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

When you want to measure how long a POST request takes in Python, the safest approach is to record time around requests.post() with time.perf_counter(). You can also inspect response.elapsed, but it represents only the request-response timing tracked by requests, not every bit of surrounding application work.

Measure end-to-end time with perf_counter

time.perf_counter() is ideal for timing short operations because it has good resolution and is meant for duration measurement.

python
1import time
2import requests
3
4url = "https://httpbin.org/post"
5payload = {"name": "Ava", "role": "tester"}
6
7start = time.perf_counter()
8response = requests.post(url, json=payload, timeout=10)
9elapsed = time.perf_counter() - start
10
11print("status:", response.status_code)
12print("seconds:", elapsed)

This captures the full client-side duration of the call, including connection setup, network transfer, server processing, and response receipt.

Compare with response.elapsed

The requests library also exposes a timing value on the response object:

python
1import requests
2
3response = requests.post(
4    "https://httpbin.org/post",
5    json={"ping": "pong"},
6    timeout=10,
7)
8
9print(response.elapsed.total_seconds())

response.elapsed is convenient, but it is not always the same number as your outer timer. If your code does JSON encoding beforehand, retries requests, or performs extra work after the response arrives, perf_counter() gives the more complete measurement.

Repeating the measurement for a more stable result

Single timings are noisy. A better pattern is to run the request several times and compute an average:

python
1import statistics
2import time
3import requests
4
5timings = []
6
7with requests.Session() as session:
8    for _ in range(5):
9        start = time.perf_counter()
10        response = session.post(
11            "https://httpbin.org/post",
12            json={"sample": True},
13            timeout=10,
14        )
15        response.raise_for_status()
16        timings.append(time.perf_counter() - start)
17
18print("mean:", statistics.mean(timings))
19print("max:", max(timings))

Using a Session is helpful because it can reuse TCP connections, which makes repeated timing measurements more realistic for a real client.

Measure server behavior, not just happy-path speed

Timing is only useful if you also know whether the response was valid. Always pair timing with status checks and error handling:

python
1import time
2import requests
3
4try:
5    start = time.perf_counter()
6    response = requests.post(
7        "https://api.example.com/orders",
8        json={"order_id": 123},
9        timeout=5,
10    )
11    response.raise_for_status()
12    elapsed = time.perf_counter() - start
13    print("successful request in", elapsed, "seconds")
14except requests.RequestException as exc:
15    print("request failed:", exc)

That way you do not accidentally treat timeout failures or HTTP 500 responses as legitimate performance samples.

Common Pitfalls

The most common mistake is measuring just once and treating that number as authoritative. Network latency varies, so one sample does not tell you much.

Another issue is forgetting timeouts. A slow or stalled server can block forever without timeout=..., which makes timing scripts unreliable.

Be careful when comparing response.elapsed with a manual timer. They answer slightly different questions, so they do not need to match exactly.

Finally, do not confuse request timing with load testing. For concurrency, throughput, and sustained traffic behavior, use a dedicated load tool rather than a single-process timing script.

If you are diagnosing an unusually slow endpoint, log both the measured duration and key response metadata such as status code, response size, and request ID. Timing without context makes it much harder to tell whether the slowdown is network-related, server-related, or tied to a specific request path.

For especially sensitive measurements, warm up the endpoint first. The first request may include extra connection setup, TLS negotiation, or cold-cache behavior that makes it unrepresentative of normal latency.

The same applies when benchmarking through proxies or VPNs.

Summary

  • Use time.perf_counter() around requests.post() to measure request duration.
  • 'response.elapsed is useful, but it is a narrower timing metric.'
  • Collect multiple samples and use a Session for more realistic repeated timings.
  • Always set a timeout and validate the response before trusting the measurement.
  • For serious performance analysis under load, move beyond one-off timing scripts.

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.