Python requests module
threading
connection pool
performance optimization
concurrency

Change the connection pool size for Python's requests module when in Threading

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 many Python threads send HTTP requests to the same host, the default connection pool settings in requests can become a bottleneck. Threads may block while waiting for an available socket, and the application can end up slower than expected even though the remote service is healthy. The usual fix is to configure a Session with a custom HTTPAdapter so the underlying urllib3 pool can hold more reusable connections.

Why the Default Pool Can Be Too Small

The requests library uses urllib3 underneath, and urllib3 manages persistent HTTP connections through connection pools. That is normally a good thing because reusing sockets reduces TCP and TLS setup overhead.

The problem appears when concurrency grows. If 20 worker threads all target the same host but the adapter only keeps a small number of reusable connections, some requests must wait or create extra churn.

A common symptom is:

  • throughput flattens out as thread count rises
  • response times become noisy under concurrency
  • profiling shows time spent waiting on network setup rather than application logic

Configure a Bigger Pool With HTTPAdapter

The practical way to increase pool capacity is to mount a custom adapter on a Session.

python
1import requests
2from requests.adapters import HTTPAdapter
3
4
5def build_session(pool_size: int) -> requests.Session:
6    session = requests.Session()
7    adapter = HTTPAdapter(
8        pool_connections=pool_size,
9        pool_maxsize=pool_size,
10        max_retries=0,
11        pool_block=True,
12    )
13    session.mount("http://", adapter)
14    session.mount("https://", adapter)
15    return session
16
17
18session = build_session(pool_size=20)
19response = session.get("https://httpbin.org/get", timeout=5)
20print(response.status_code)

The key options are:

  • 'pool_maxsize for the maximum number of connections per pool'
  • 'pool_connections for the number of pools cached by the adapter'
  • 'pool_block=True so threads wait for a connection instead of creating unbounded churn'

Use the Session in a Threaded Workflow

Here is a small example using ThreadPoolExecutor:

python
1from concurrent.futures import ThreadPoolExecutor
2import requests
3from requests.adapters import HTTPAdapter
4
5
6session = requests.Session()
7adapter = HTTPAdapter(pool_connections=20, pool_maxsize=20, pool_block=True)
8session.mount("https://", adapter)
9session.mount("http://", adapter)
10
11
12def fetch(url: str) -> int:
13    response = session.get(url, timeout=5)
14    return response.status_code
15
16
17urls = ["https://httpbin.org/get"] * 20
18
19with ThreadPoolExecutor(max_workers=20) as executor:
20    results = list(executor.map(fetch, urls))
21
22print(results)

This pattern works well when many threads reuse the same host and benefit from a warm pool of persistent connections.

Pick the Pool Size Intentionally

A good starting point is to match pool_maxsize to the number of concurrent requests you expect against the same host. If you have 16 worker threads that mostly target one API, a pool near 16 is a reasonable first test.

That does not mean bigger is always better. A very large pool can waste resources or exceed practical limits on the client or server side. Tune based on:

  • number of worker threads
  • number of target hosts
  • average request duration
  • server-side rate limits and keep-alive behavior

Measure under realistic load instead of guessing.

Shared Session or One Session Per Thread

This is where teams differ. The underlying urllib3 pool is designed for concurrent use, but a requests.Session also contains mutable state such as cookies and headers. If many threads are mutating that state, sharing one session can become risky.

A safe rule is:

  • shared session is fine when configuration is set once and then treated as read-only
  • per-thread sessions are simpler if threads need independent auth, cookies, or mutable headers

If you choose one session per thread, you can still configure the adapter the same way. The tradeoff is fewer shared connections across threads.

Always Set Timeouts

A larger pool does not solve hangs caused by missing timeouts. In threaded code, every request should set explicit connect and read timeouts so stuck workers do not occupy pool slots forever.

python
1response = session.get(
2    "https://httpbin.org/delay/1",
3    timeout=(2, 5),
4)

That keeps the connection pool healthy under failure conditions.

Common Pitfalls

The first mistake is increasing the thread count without increasing pool capacity. More threads alone do not improve throughput if they all fight for the same small pool.

Another issue is leaving pool_block at its default behavior and then wondering why connection usage becomes erratic under pressure. Blocking is often easier to reason about than unchecked connection churn.

Developers also share a session across threads while mutating cookies or headers dynamically. The pool may be fine, but the session state becomes the real problem.

Finally, do not forget timeouts. A few stuck requests can make a properly sized pool behave like an undersized one.

Summary

  • 'requests uses urllib3 connection pools, and the defaults may be too small for threaded workloads.'
  • Increase pool capacity by mounting a custom HTTPAdapter on a Session.
  • Size pool_maxsize around the expected concurrent requests per host.
  • Treat a shared session as effectively read-only, or use separate sessions per thread when state differs.
  • Combine pooling with explicit timeouts so blocked requests do not exhaust the pool.

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