Dequest
Dequest is a full featured declarative HTTP client for Python that simplifies the creation and sending HTTP requests and retrieves the results as DTO.
Why dequest?
-
Fast to code: Develop API-integrated features 2–3× faster with zero boilerplate—just decorate and go.
-
No boilerplate: Say goodbye to manual
requests,asyncio, retries, and JSON parsing. One function = one clean API call. -
Safe and typed: Supports Pydantic-style DTOs, typed parameters, and response mapping—minimizing runtime surprises.
-
Resilient by default: Built-in support for retries, caching, and circuit breakers. Your APIs won’t bring your app down.
-
Declarative and elegant: Define path, query, form, and body parameters cleanly with
PathParameter,QueryParameter,JsonBody, etc. -
Smart caching: Add in-memory or Redis-based caching with a single flag.
-
Async without async: Use
@async_clientand fire off non-blocking API calls without dealing with asyncio yourself. -
Built-in callbacks & fallbacks: Handle async responses or fallback gracefully when APIs are flaky, dequest makes it smooth.
-
Framework-agnostic: Works in any Python project, from CLI apps to FastAPI, Flask, Django, and beyond.
Key Features
✅ Supports GET, POST, PUT, PATCH and DELETE requests
✅ Sync & Async Client
✅ Optional Caching for GET Requests (Support In-Memory, Redis, Django Cache)
✅ Support authentication (Static & Dynamic)
✅ Maps API Json/XML response to DTO object and list (Supports unlimited nested DTOs)
✅ Support query parameters, JSON body, Form-data and File uploads
✅ Retry and Backoff
✅ Allows Custom Headers per Request (Static & Dynamic)
✅ Circuit Breaker with Custom Fallback Function
✅ API parameter mapping and type checking
✅ Logging
Installation
To install Dequest, simply run:
pip install dequest
Getting Started
Synchronous API Calls
Use @sync_client to make synchronous HTTP requests without writing boilerplate code:
from dequest import sync_client, QueryParameter
from typing import List
from dataclasses import dataclass
@dataclass
class UserDto:
id: int
name: str
city: str
@sync_client(url="https://jsonplaceholder.typicode.com/users", dto_class=UserDto)
def get_users(city: str = QueryParameter(default="Paris", alias="city_name")) -> List[UserDto]:
pass
users = get_users(city="New York")
print(users)
Asynchronous Fire-and-Forget API Calls
Use @async_client to make non-blocking fire-and-forget HTTP requests in synchronous/asynchronous code, with callback for handling responses:
from dequest import async_client, HTTPMethod
async def callback_function(response):
print(response)
@async_client(url="https://api.example.com/notify", method=HTTPMethod.POST, callback=callback_function)
def notify():
pass
notify()
Awaitable Asynchronous API Calls
Use @async_await_client to make asynchronous HTTP requests that can be awaited for their result:
import asyncio
from dequest import async_await_client, QueryParameter
@async_await_client(url="https://jsonplaceholder.typicode.com/users", dto_class=UserDto)
async def get_users_async(city: str = QueryParameter(default="Paris", alias="city_name")) -> List[UserDto]:
pass
async def main():
users = await get_users_async(city="New York")
print(users)
asyncio.run(main())
Handling Parameters
Path Parameters
Pass values inside the URL using PathParameter:
from dequest import sync_client, PathParameter
@sync_client(url="https://jsonplaceholder.typicode.com/users/{user_id}", dto_class=UserDto)
def get_user(user_id: int = PathParameter()) -> UserDto:
pass
user = get_user(user_id=1)
print(user)
Query Parameters
Pass values as URL query parameters using QueryParameter:
from dequest import sync_client, QueryParameter
@sync_client(url="https://api.example.com/search", dto_class=UserDto)
def search_users(name: str = QueryParameter()):
pass
users = search_users(name="Alice")
JSON Parameters
For POST requests pass values as JSON payload using JsonBody:
from dequest import sync_client, HTTPMethod, JsonBody
@sync_client(url="https://api.example.com/users", method=HTTPMethod.POST, dto_class=UserDto)
def create_user(name: str = JsonBody(), city: str = JsonBody()) -> UserDto:
pass
new_user = create_user(name="Alice", city="Berlin")
Form Parameters
For POST/PUT requests pass values as form data using FormParameter:
from dequest import sync_client, HTTPMethod, FormParameter
@sync_client(url="https://api.example.com/users", method=HTTPMethod.POST, dto_class=UserDto)
def create_user(name: str = FormParameter(), city: str = FormParameter()) -> UserDto:
pass
new_user = create_user(name="Alice", city="Berlin")
File Uploads
Upload files along with form data using FileParameter. Supports bytes, file paths, and file-like objects:
from dequest import sync_client, HTTPMethod, FileParameter, FormParameter
@sync_client(url="https://api.example.com/upload", method=HTTPMethod.POST, dto_class=dict)
def upload_file(file: bytes = FileParameter(), description: str = FormParameter()) -> dict:
pass
# Upload with bytes
upload_file(file=b"file content", description="My document")
# FileParameter also supports:
# - File paths: upload_file(file="/path/to/file.pdf", description="...")
# - File-like objects: upload_file(file=open("file.pdf", "rb"), description="...")
Note: FileParameter automatically converts the request to multipart/form-data format. Cannot be combined with JsonBody.
Deprecated Parameter Syntax
The old subscription-based parameter style is deprecated and will be removed in a future release.
Deprecated style:
from dequest import sync_client, QueryParameter
@sync_client(url="https://api.example.com/search")
def search_users(name: QueryParameter[str, "fullname"]):
pass
Use the new FastAPI-style declaration instead:
from dequest import sync_client, QueryParameter
@sync_client(url="https://api.example.com/search")
def search_users(name: str = QueryParameter(alias="fullname")):
pass
Notes:
- The Python type now comes from the normal annotation, such as
name: str. - Mapping options such as
aliasanddefaultnow go insideQueryParameter(...),PathParameter(...),FormParameter(...), orJsonBody(...). - If old-style subscription syntax is still used, Dequest emits a
FutureWarningto help identify code that should be migrated.
Advanced Features
Retries
Automatically retry failed requests on specified exceptions:
from dequest.exceptions import HTTPError, ConnectTimeout
@sync_client(url="https://api.example.com/data", retries=3, retry_on_exceptions=(HTTPError, ConnectTimeout), retry_delay=2)
def get_data():
pass
Caching
Enable caching for GET requests:
@sync_client(url="https://api.example.com/popular-posts", enable_cache=True, cache_ttl=60)
def get_popular_posts():
pass
Circuit Breaker
Prevent excessive calls to failing APIs using a circuit breaker:
from dequest import sync_client, CircuitBreaker
breaker = CircuitBreaker(failure_threshold=5, recovery_timeout=30)
@sync_client(url="https://api.unstable.com/data", circuit_breaker=breaker)
def get_unstable_data():
pass
Fallback on Failure
Define a fallback function for when the circuit breaker is open:
from dequest import CircuitBreaker
def fallback_response():
return {"message": "Service unavailable, returning cached data"}
breaker = CircuitBreaker(failure_threshold=3, recovery_timeout=10, fallback_function=fallback_response)
@sync_client(url="https://api.unstable.com/data", circuit_breaker=breaker)
def fetch_unstable_data():
pass
Configuration
Dequest allows global configuration via DequestConfig, the configuration can be set using .config method of the DequestConfig class:
from dequest import DequestConfig
DequestConfig.config(
cache_provider=CacheProvider.REDIS, # defaults is "in_memory"
redis_host="my-redis-server",
redis_port=6380,
redis_db=1,
redis_password="securepassword",
redis_ssl=True,
)
Documentation
For comprehensive details on Dequest, please refer to the full documentation available at Read the Docs.
License
Dequest is released under the BSD 3-Clause License.
Metadata
Release files for dequest 0.8.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| dequest-0.8.0.tar.gz | 28.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| dequest-0.8.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 64.0 kB
Release files / dequest-0.8.0.tar.gz
| Download URL | dequest-0.8.0.tar.gz |
|---|---|
| Size | 28.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b2e6b4e70506f2c3dc6876e6d688f7c1cf4d01b4245b7ed55362e487c783e95c
|
|
BLAKE2b-256 checksum How to use checksums |
6ef095de43be572c4dfb4996d996dc76d72632f8bea3737017ff8f0457669604
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.9
|
Release files / dequest-0.8.0-py3-none-any.whl
| Download URL | dequest-0.8.0-py3-none-any.whl |
|---|---|
| Size | 35.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
06ed86c03f81d4ddd30240b7c1936c2c8bbaebf91fb9a85e4fee260352baec2a
|
|
BLAKE2b-256 checksum How to use checksums |
880972b65fa979f58a2c3ae419b2cbe55a47719ff68a2840ceff762110132618
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.9
|