Using HTTPX vs Requests
Relevant source files
The following files were used as context for generating this wiki page:
The Resend Python SDK provides two distinct HTTP client implementations to support both synchronous and asynchronous execution. The RequestsClient uses the requests library for standard synchronous operations, while the HTTPXClient utilizes the httpx library to handle asynchronous requests.
This dual-client architecture allows you to choose the execution model that fits your application's requirements. Synchronous calls operate in a blocking manner by default, while asynchronous calls allow for non-blocking I/O when the httpx dependency is present.
Sources: resend/init.py:180-190, resend/http_client_requests.py, resend/http_client_httpx.py
Client Architecture and Interfaces
The SDK defines two primary interfaces for HTTP communication. Synchronous clients implement the HTTPClient interface, and asynchronous clients implement the AsyncHTTPClient interface.
Client Hierarchy
The following diagram illustrates the relationship between the base interfaces and their concrete implementations:
This diagram shows the inheritance structure of the SDK's HTTP clients.
Sources: resend/http_client_async.py:6-12, resend/init.py:287-295
Synchronous Execution with Requests
The RequestsClient is the default provider for synchronous requests. It is initialized when the library is imported and assigned to resend.default_http_client.
- Default Use: Every standard API call (e.g.,
resend.Emails.send()) triggers theRequestclass. - Mechanism: The
Request.make_request()method retrieves thedefault_http_client, casts it toHTTPClient, and executes the request. - Initialization: The SDK sets
default_http_client = RequestsClient()by default in the package root.
Sources: resend/request.py:102-120, resend/init.py:183
Asynchronous Execution with HTTPX
Asynchronous operations require the httpx library. If httpx is installed, the SDK automatically detects it and configures the HTTPXClient.
Selection and Detection Logic
When you call an async method (e.g., resend.Emails.send_async()), the AsyncRequest class determines which client to use based on a priority hierarchy:
- Explicit Dedicated Client: An manually assigned
resend.default_async_http_client. - Legacy Custom Client: A custom client assigned to
resend.default_http_clientthat happens to implementAsyncHTTPClient. - Automatic Default: The auto-detected
HTTPXClient.
This flowchart describes how the SDK selects an asynchronous HTTP client at runtime.
Sources: resend/async_request.py:75-87, resend/init.py:287-296, tests/async_http_client_selection_test.py:117-150
Client Comparison Summary
| Feature | RequestsClient (Sync) | HTTPXClient (Async) |
|---|---|---|
| Underlying Library | requests | httpx |
| Default Assignment | resend.default_http_client | resend.default_async_http_client |
| Installation | Required by default | Optional (use resend[async]) |
| Usage Context | Standard blocking calls | await calls and *_async methods |
| Detection | Always active | Auto-detected if importable |
Sources: resend/init.py:183-296, resend/request.py, resend/async_request.py
Configuration and Overrides
You can override the default clients by assigning new instances to the global configuration variables. This is useful for providing custom timeouts, proxies, or logging middleware.
Global Configuration Variables
resend.default_http_client: Holds the implementation for synchronous requests. Defaults toRequestsClient().resend.default_async_http_client: Holds the implementation for asynchronous requests. Defaults toHTTPXClient()if available, otherwiseNone.
If default_async_http_client is None and an async operation is attempted, the SDK raises a ResendError with the type AsyncClientNotConfigured.
Sources: resend/init.py:183-190, resend/async_request.py:89-94
Example: Async Request Flow
This sequence diagram shows the internal data flow during an asynchronous API call.
Sources: resend/async_request.py:30-100, resend/http_client_httpx.py
Technical Constraints and Requirements
- JSON Requirement: Both clients enforce JSON responses. If a non-JSON response is received (e.g., HTML from a CDN or proxy), the clients raise an
application_errorwith the corresponding HTTP status code. - Status Code Handling: Successful requests must return a status code of 200. Any other code (or a 200 code paired with a body-level error status) triggers an exception.
- Headers: The SDK automatically injects
Accept,Authorization, andUser-Agentheaders into every request, regardless of the client used.
Sources: resend/request.py:53-75, resend/async_request.py:53-73, resend/request.py:145-155
The SDK's choice between HTTPX and Requests allows for a high degree of flexibility. Use Requests for simple scripts and standard applications, and install HTTPX for high-concurrency environments or modern async-first frameworks.