robotframework-parallel-requests
A Robot Framework library for parallelized HTTP requests using httpx and ThreadPool.
Queue multiple HTTP requests and run them in parallel, then retrieve responses by ID or await all responses. Designed for testing scenarios like rate limiting, bulk API operations, and performance validation.
Status
⚠️ Beta — Core functionality stable; API may evolve.
Current production scope uses synchronous httpx transport with ThreadPool concurrency
(connection pool sized to worker count; optional HTTP/2). Native async transport is planned
for a later release.
Features
- Parallelized requests: Queue up multiple HTTP requests and execute them concurrently using ThreadPoolExecutor.
- RequestsLibrary-like API: Keywords mirror RequestsLibrary for familiarity (with
Parallelprefix). - Direct response access: Retrieve the underlying
httpx.Responseobject for advanced assertions. - Simple session management: Create named sessions with base URLs and default headers.
Table of Contents
- Installation
- Quick Start
- Comparison with RequestsLibrary
- Keywords Reference
- Library Initialization
- Use Cases
- Error Handling
- Best Practices
- Architecture
- Testing
- Future Enhancements
- Contributing
- Release Process
- License
Installation
From PyPI:
pip install robotframework-parallel-requests
Optional HTTP/2 support:
pip install robotframework-parallel-requests[http2]
Development/Local Installation:
pip install -r requirements.txt
pip install -e .
Prerequisites:
- Python 3.8+
- Robot Framework 4.0+
Dependencies:
httpx>=0.23.0- HTTP clientrobotframework>=4.0- Robot Framework corepytest>=7.0- Testing (dev only)respx>=0.20.0- httpx mocking (dev only)
Quick Start
Basic Usage
*** Settings ***
Library robot_parallel_requests
*** Test Cases ***
Queue And Wait For Responses
Parallel Create Session alias=default base_url=https://api.example.com
${id1}= Parallel Queue Request GET /users/1
${id2}= Parallel Queue Request GET /users/2
${id3}= Parallel Queue Request GET /users/3
Parallel Wait For All Requests timeout=30
${status1}= Parallel Get Response Status ${id1}
Should Be Equal ${status1} 200
${body}= Parallel Get Response Body ${id1}
Log ${body}
Parallel Shutdown
Using Response Objects Directly
*** Test Cases ***
Access Response Object
Parallel Create Session
${id}= Parallel Queue Request GET https://httpbin.org/json
Parallel Wait For All Requests timeout=30
${response}= Parallel Get Response Object ${id}
# Now you have the underlying httpx.Response object
Should Be Equal ${response.status_code} 200
${json_data}= Parallel Get Response JSON ${id}
Log ${json_data}
Parallel Shutdown
Comparison with RequestsLibrary
| Feature | robot_parallel_requests | RequestsLibrary |
|---|---|---|
| Parallel/Async Requests | ✅ Native ThreadPoolExecutor | ❌ Sequential only |
| Queue Multiple Requests | ✅ Yes, with ID-based retrieval | ❌ No |
| Bulk Operations | ✅ Optimized | ❌ Requires loops + waits |
| Rate Limiting Tests | ✅ Token bucket with per-send throttling | ⚠️ Difficult/slow |
| API Compatibility | Similar keywords (with Parallel prefix) |
— |
| Direct Response Objects | ✅ httpx.Response access |
✅ requests.Response access |
| Session Management | ✅ Named sessions | ✅ Named sessions |
When to use robot_parallel_requests:
- Testing APIs with rate limits, quotas, or concurrency requirements
- Bulk operations (e.g., creating 100 records in parallel)
- Performance/load testing within Robot Framework
- Simulating real-world parallel client behavior
- Optimizing tests that make many requests in succession
When to stick with RequestsLibrary:
- Simple sequential API testing
- Lightweight HTTP assertions
- No parallel workload requirements
- But feel free to use both—we're good with that
Error Handling
When a request fails (network error, timeout, invalid URL), the exception is stored in the response store:
*** Test Cases ***
Handle Request Failures
Parallel Create Session
${id1}= Parallel Queue Request GET https://httpbin.org/delay/2 timeout=1
${id2}= Parallel Queue Request GET https://invalid-domain-12345.com
Parallel Wait For All Requests timeout=10
# Check if response is an exception
${resp}= Parallel Get Response Object ${id1}
Run Keyword If '${type(resp).__name__}' == 'ReadTimeout' Log Request timed out
# For a regular response, status code is safe
${resp2}= Parallel Get Response Object ${id2}
Run Keyword If '${type(resp2).__name__}' == 'ConnectError' Log Request failed: ${resp2}
Parallel Shutdown
Safe Patterns:
- Always call
Parallel Wait For All Requestsbefore retrieving responses - Use
Parallel Get Response Objectand check the exception type if needed - Use
Run Keyword Ifwith type checks for conditional error handling
Best Practices
Library Scope:
- This library sets
ROBOT_LIBRARY_SCOPE = "TEST"so each test gets a fresh instance and resources are always isolated. - This is the most idiomatic and robust approach for libraries managing connections, pools, or other stateful resources.
Shutdown Handling:
- Shutdown runs automatically at end of each test via the library listener.
- Explicit
Parallel Shutdownis still safe (idempotent) and useful mid-test. - ❌ Bad: Calling
Parallel Shutdownmid-test and then queueing more requests withoutParallel Set Worker Countto recreate the pool.
Worker Count:
- Default
worker_count=5is suitable for most scenarios. - For high-throughput tests, increase to 10-20 (connection pool limits scale with workers).
- For I/O-heavy operations, ThreadPoolExecutor can handle 50+ safely.
- Enable
http2=True(optionalh2extra) if you want multiplexing to a single origin.
Timeout Handling:
- Always set explicit timeouts in
Parallel Wait For All Requeststo prevent hanging tests. - Individual request timeouts (via
timeout=parameter inQueue Request) affect only that request. - By default, if the wait timeout expires, the keyword logs a warning. Use
fail_on_timeout=${True}(keyword or library init) to fail the test instead. - Not-yet-started futures are cancelled on timeout; in-flight HTTP calls may still finish in the background.
Sessions:
- Omitting
session=automatically uses thedefaultsession when one was created. - Pass
session=<alias>for non-default sessions.
Request IDs:
- Each queued request must use a unique
idwhen you provide a custom value. Reusing an ID raisesValueError.
Batch waits:
Parallel Wait For All And Get Responsesreturns only the requests queued since the previous wait in the same test.
Rate limiting:
- Throttling is enforced when requests are sent, not when they are queued.
- Default burst size is
requests + 1(e.g.105 per="minute"→ burst106). - Retries consume rate-limit tokens on each HTTP attempt.
If you override library scope:
- Use
Suite Teardownto call shutdown/cleanup keywords. - Avoid calling shutdown in individual tests unless you fully understand the implications.
Keywords
Session Management
Parallel Create Session
- Arguments:
alias(str, defaultdefault),base_url(str, optional),headers(dict, optional) - Description: Create a named session with optional base URL and default headers. The
defaultsession is applied automatically when queue keywords omitsession=.
Parallel Shutdown
- Description: Shutdown worker pool and close transport (also runs automatically at end of each test).
Request Queuing
Parallel Queue Request
- Arguments:
method(str),url(str),session(str, optional),id(str, optional),**kwargs(headers, json, data, params, etc.) - Returns: Response ID (string)
- Description: Queue a request to be processed by the worker pool. Returns a response ID for later retrieval. Custom
idvalues must be unique within the test instance.
Parallel Queue Many
- Arguments:
requests(list of dicts or[method, url]pairs),session(str, optional) - Returns: List of response IDs
- Description: Bulk-enqueue many requests and return their IDs in order.
Parallel Start Workers
- Description: Start worker pool (workers are ready on init; this is a no-op in MVP).
Parallel Wait For All Requests
- Arguments:
timeout(float, optional, seconds),fail_on_timeout(bool, optional) - Description: Block until all requests queued since the last wait complete or the timeout expires. Logs a warning (or raises
TimeoutErrorwhen fail-on-timeout is enabled) if any requests remain incomplete.
Parallel Wait For All And Get Responses
- Arguments:
timeout(float, optional, seconds),fail_on_timeout(bool, optional) - Returns: List of
httpx.Responseor Exception objects in submission order for the current pending batch only.
Response Retrieval
Parallel Get Response Object
- Arguments:
id(str) - Returns:
httpx.Responseobject (or Exception if request failed) - Description: Retrieve the underlying response object for direct assertions.
Parallel Get Response Status
- Arguments:
id(str) - Returns: Status code (int)
Parallel Get Response Body
- Arguments:
id(str) - Returns: Response body as string
Parallel Get Response JSON
- Arguments:
id(str) - Returns: Parsed JSON (dict/list)
Configuration
Parallel Set Worker Count
- Arguments:
count(int) - Description: Adjust the number of concurrent worker threads.
Parallel Set Rate Limit
- Arguments:
requests(float),per(str, default"second"),burst_size(int, optional) - Description: Configure client-side token-bucket rate limiting. Default burst size is
requests + 1.
Parallel Set Retry Policy
- Arguments:
max_retries,backoff_factor,retry_statuses,jitter - Description: Configure exponential backoff retries for selected HTTP status codes and transport errors (timeouts/network). Jitter is applied to backoff waits.
Parallel Get Metrics
- Description: Return aggregated request metrics including retry counts and requests per second.
Library Initialization
Arguments
worker_count(int, default:5): Number of worker threads in the pool (connection limits follow).fail_on_timeout(bool, default:False): Make wait keywords raiseTimeoutErrorwhen incomplete.http2(bool, default:False): Enable HTTP/2 (requires optionalh2extra).cancel_pending_on_timeout(bool, default:True): Cancel not-yet-started futures when a wait times out.
Examples
# Default: 5 worker threads
Library robot_parallel_requests
# High concurrency: 20 worker threads, fail waits on timeout
Library robot_parallel_requests worker_count=20 fail_on_timeout=${True}
Use Cases
1. Rate Limiting Testing
Queue 101 requests (where the 101st should fail) to test error handling and rate limits:
*** Test Cases ***
Test Rate Limit With Bulk Requests
Parallel Create Session
FOR ${i} IN RANGE 101
${id}= Parallel Queue Request POST /api/favorite-restaurants json={"name": "Restaurant ${i}"}
END
Parallel Wait For All Requests timeout=60
# Verify 100 succeeded, 1 failed
# ... retrieve and check statuses
2. Parallel API Calls
Fetch multiple user profiles in parallel:
*** Test Cases ***
Fetch Multiple User Profiles
Parallel Create Session base_url=https://api.example.com
@{user_ids}= Create List 1 2 3 4 5
@{response_ids}= Create List
FOR ${user_id} IN @{user_ids}
${id}= Parallel Queue Request GET /users/${user_id}
Append To List @{response_ids} ${id}
END
Parallel Wait For All Requests timeout=30
FOR ${id} IN @{response_ids}
${resp}= Parallel Get Response Object ${id}
Should Be Equal ${resp.status_code} 200
END
Parallel Shutdown
3. Direct Response Assertions
Use the raw response object for complex assertions:
*** Test Cases ***
Advanced Response Assertions
Parallel Create Session
${id}= Parallel Queue Request GET https://httpbin.org/headers
Parallel Wait For All Requests
${resp}= Parallel Get Response Object ${id}
Should Contain ${resp.headers['user-agent']} python-httpx
Should Be Equal As Numbers ${resp.elapsed.total_seconds()} ${0} delta=5
Parallel Shutdown
Testing
Run unit tests:
pytest tests/ -v
Run example Robot tests (requires Robot Framework installed in venv):
robot examples/parallel_requests.robot
Architecture
tasks.py:RequestTaskdataclass for queued requests.response_store.py: In-memory storage for responses by ID.transport/base.py: Abstract transport interface.transport/httpx_sync.py: Synchronous httpx-based transport.worker.py: ThreadPoolExecutor-based worker pool.library.py: Robot Framework library with keywords.
Future Enhancements
- Async transport (
transport/httpx_async.py) usinghttpx.AsyncClientfor very high concurrency. - Circuit breaker / adaptive backoff to complement existing retry policy.
- Structured logging / tracing hooks (OpenTelemetry integration).
- Per-session prioritized queues for differentiated QoS.
- Optional persistent response cache (configurable TTL).
Contributing
Contributions are welcome. See CONTRIBUTING.md for setup, branching, and pull request expectations.
License
MIT — see LICENSE.
Release Process
Automated releases are driven by Git tags and a GitHub Actions workflow (publish.yml).
Version Tags (PEP 440)
| Type | Example | Notes |
|---|---|---|
| Final | v0.1.0 |
Stable release consumers get by default |
| Release Candidate | v0.1.0rc1 |
Treated as prerelease; published to PyPI & TestPyPI |
| Beta / Alpha | v0.1.0b1, v0.1.0a1 |
Published to TestPyPI only |
| Dev Snapshot | v0.1.0.dev2 |
Iterative build, TestPyPI only |
Publishing Matrix
| Tag Type | TestPyPI | PyPI | GitHub Release | Prerelease Flag |
|---|---|---|---|---|
Final (vX.Y.Z) |
No | Yes | Yes | No |
RC (vX.Y.ZrcN) |
Yes | Yes | Yes | Yes |
Beta/Alpha (vX.Y.ZbN/aN) |
Yes | No | Yes | Yes |
Dev (vX.Y.Z.devN) |
Yes | No | Yes | Yes |
Changelog Enforcement
Final and RC tags must have a ## [X.Y.Z] section in CHANGELOG.md. Missing sections cause the workflow to fail.
Prerelease Notes Generation
For dev/alpha/beta tags, release notes are generated from the commit diff between the new tag and the previous v* tag:
### v0.1.0b1
Changes since v0.1.0:
- abc123 Short commit message (Author)
Trusted Publishing
Configure trusted publishers with workflow file publish.yml.
| Tag type | GitHub environment |
|---|---|
| Dev / alpha / beta | testpypi |
| RC | testpypi and pypi |
| Final | pypi |
Release Steps
- Update
CHANGELOG.md(for final/RC). - Run tests:
pytest -vandrobot examples/parallel_requests.robot. - Ensure the matching GitHub environment(s) and PyPI/TestPyPI trusted publishers exist.
- Tag and push:
git tag v0.1.0rc1 git push origin v0.1.0rc1 # Final: git tag v0.1.0 git push origin v0.1.0
- Workflow builds, uploads, generates notes, creates GitHub Release.
Installing Pre-Releases
pip install --pre robotframework-parallel-requests
# Or pin a specific build
pip install robotframework-parallel-requests==0.1.0rc1
TestPyPI Validation
pip install -i https://test.pypi.org/simple robotframework-parallel-requests==0.1.0.dev2 --extra-index-url https://pypi.org/simple
Patch / Hotfix
Add a new section in CHANGELOG, tag, push:
git tag v0.1.1
git push origin v0.1.1
Yanks & Post Releases
If a bad release ships, yank on PyPI and publish X.Y.Z.post1 with the fix.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file robotframework_parallel_requests-0.1.0.tar.gz.
File metadata
- Download URL: robotframework_parallel_requests-0.1.0.tar.gz
- Upload date:
- Size: 31.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ef82df51ac993f73002096032626b7738280106c7c00939e54a8e60981535788
|
|
| MD5 |
74fcef6af410c33c164ade5c51792f8c
|
|
| BLAKE2b-256 |
b35fe8511faf9589f9c4b27aba074622c9e6d77700dbb259951ad999e13fa6c5
|
Provenance
The following attestation bundles were made for robotframework_parallel_requests-0.1.0.tar.gz:
Publisher:
publish.yml on tallin32/robotframework-parallel-requests
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
robotframework_parallel_requests-0.1.0.tar.gz -
Subject digest:
ef82df51ac993f73002096032626b7738280106c7c00939e54a8e60981535788 - Sigstore transparency entry: 2732622869
- Sigstore integration time:
-
Permalink:
tallin32/robotframework-parallel-requests@cd3283f52a960b857a4ea29e09bcd35bb8f8a09e -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/tallin32
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@cd3283f52a960b857a4ea29e09bcd35bb8f8a09e -
Trigger Event:
push
-
Statement type:
File details
Details for the file robotframework_parallel_requests-0.1.0-py3-none-any.whl.
File metadata
- Download URL: robotframework_parallel_requests-0.1.0-py3-none-any.whl
- Upload date:
- Size: 24.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
286a10f8568ee921a28b37ab86da7c4866d13816dc0312cfa4e773984ce2be38
|
|
| MD5 |
4a4562ea7f08b800e9cdf7b3b75f7b78
|
|
| BLAKE2b-256 |
02bed3041edec104316b8b384b5e32f41461b70c5625d99b48cc4b67b46cc962
|
Provenance
The following attestation bundles were made for robotframework_parallel_requests-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on tallin32/robotframework-parallel-requests
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
robotframework_parallel_requests-0.1.0-py3-none-any.whl -
Subject digest:
286a10f8568ee921a28b37ab86da7c4866d13816dc0312cfa4e773984ce2be38 - Sigstore transparency entry: 2732622981
- Sigstore integration time:
-
Permalink:
tallin32/robotframework-parallel-requests@cd3283f52a960b857a4ea29e09bcd35bb8f8a09e -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/tallin32
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@cd3283f52a960b857a4ea29e09bcd35bb8f8a09e -
Trigger Event:
push
-
Statement type: