Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions examples/domains.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@
print(f"Has more domains: {domains['has_more']}")
if not domains["data"]:
print("No domains found")
for domain in domains["data"]:
print(domain)
for listed_domain in domains["data"]:
print(listed_domain)

print("\n--- Using pagination parameters ---")
if domains["data"]:
Expand Down
70 changes: 70 additions & 0 deletions examples/with_headers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
"""
Example demonstrating how to access response headers.

Response headers include useful information like rate limits, request IDs, etc.
"""

import os

import resend

if not os.environ["RESEND_API_KEY"]:
raise EnvironmentError("RESEND_API_KEY is missing")

params: resend.Emails.SendParams = {
"from": "onboarding@resend.dev",
"to": ["delivered@resend.dev"],
"subject": "Hello from Resend",
"html": "<strong>Hello, world!</strong>",
}

print("=" * 60)
print("Example 1: Without type annotations")
print("=" * 60)

response = resend.Emails.send(params)
print(f"Email sent! ID: {response['id']}")
print(f"Request ID: {response['headers'].get('x-request-id')}")
print(f"Rate limit: {response['headers'].get('x-ratelimit-limit')}")
print(f"Rate limit remaining: {response['headers'].get('x-ratelimit-remaining')}")
print(f"Rate limit reset: {response['headers'].get('x-ratelimit-reset')}")

print("\n" + "=" * 60)
print("Example 2: With type annotations")
print("=" * 60)

typed_response: resend.Emails.SendResponse = resend.Emails.send(params)
print(f"Email sent! ID: {typed_response['id']}")

if "headers" in typed_response:
print(f"Request ID: {typed_response['headers'].get('x-request-id')}")
print(f"Rate limit: {typed_response['headers'].get('x-ratelimit-limit')}")
print(
f"Rate limit remaining: {typed_response['headers'].get('x-ratelimit-remaining')}"
)
print(f"Rate limit reset: {typed_response['headers'].get('x-ratelimit-reset')}")

print("\n" + "=" * 60)
print("Example 3: Rate limit tracking")
print("=" * 60)


def send_with_rate_limit_check(params: resend.Emails.SendParams) -> str:
"""Example function showing how to track rate limits."""
response = resend.Emails.send(params)

# Access headers via dict key
headers = response.get("headers", {})
remaining = headers.get("x-ratelimit-remaining")
limit = headers.get("x-ratelimit-limit")

if remaining and limit:
print(f"Rate limit usage: {int(limit) - int(remaining)}/{limit}")
if int(remaining) < 10:
print("⚠️ Warning: Approaching rate limit!")

return response["id"]


email_id = send_with_rate_limit_check(params)
print(f"Sent email with ID: {email_id}")
16 changes: 16 additions & 0 deletions resend/_base_response.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"""Base response type for all Resend API responses."""

from typing import Dict

from typing_extensions import NotRequired, TypedDict


class BaseResponse(TypedDict):
"""Base response type that all API responses inherit from.

Attributes:
headers: HTTP response headers including rate limit info, request IDs, etc.
Optional field that may not be present in all responses.
"""

headers: NotRequired[Dict[str, str]]
5 changes: 3 additions & 2 deletions resend/api_keys/_api_keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@
from typing_extensions import NotRequired, TypedDict

from resend import request
from resend._base_response import BaseResponse
from resend.api_keys._api_key import ApiKey
from resend.pagination_helper import PaginationHelper


class ApiKeys:

class ListResponse(TypedDict):
class ListResponse(BaseResponse):
"""
ListResponse type that wraps a list of API key objects with pagination metadata

Expand All @@ -32,7 +33,7 @@ class ListResponse(TypedDict):
Whether there are more results available for pagination
"""

class CreateApiKeyResponse(TypedDict):
class CreateApiKeyResponse(BaseResponse):
"""
CreateApiKeyResponse is the type that wraps the response of the API key that was created

Expand Down
7 changes: 4 additions & 3 deletions resend/audiences/_audiences.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@

from typing_extensions import NotRequired, TypedDict

from resend._base_response import BaseResponse
from resend.segments._segments import Segments

from ._audience import Audience


class Audiences:

class RemoveAudienceResponse(TypedDict):
class RemoveAudienceResponse(BaseResponse):
"""
RemoveAudienceResponse is the type that wraps the response of the audience that was removed

Expand Down Expand Up @@ -51,7 +52,7 @@ class ListParams(TypedDict):
Cannot be used with the after parameter.
"""

class ListResponse(TypedDict):
class ListResponse(BaseResponse):
"""
ListResponse type that wraps a list of audience objects with pagination metadata

Expand All @@ -74,7 +75,7 @@ class ListResponse(TypedDict):
Whether there are more results available for pagination
"""

class CreateAudienceResponse(TypedDict):
class CreateAudienceResponse(BaseResponse):
"""
CreateAudienceResponse is the type that wraps the response of the audience that was created

Expand Down
9 changes: 5 additions & 4 deletions resend/broadcasts/_broadcasts.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from typing_extensions import NotRequired, TypedDict

from resend import request
from resend._base_response import BaseResponse
from resend.pagination_helper import PaginationHelper

from ._broadcast import Broadcast
Expand Down Expand Up @@ -143,7 +144,7 @@ class SendParams(TypedDict):
The date should be in natural language (e.g.: in 1 min) or ISO 8601 format (e.g: 2024-08-05T11:52:01.858Z).
"""

class CreateResponse(TypedDict):
class CreateResponse(BaseResponse):
"""
CreateResponse is the class that wraps the response of the create method.

Expand All @@ -156,7 +157,7 @@ class CreateResponse(TypedDict):
id of the created broadcast
"""

class UpdateResponse(TypedDict):
class UpdateResponse(BaseResponse):
"""
UpdateResponse is the class that wraps the response of the update method.

Expand Down Expand Up @@ -195,7 +196,7 @@ class ListParams(TypedDict):
Cannot be used with the after parameter.
"""

class ListResponse(TypedDict):
class ListResponse(BaseResponse):
"""
ListResponse is the class that wraps the response of the list method with pagination metadata.

Expand All @@ -218,7 +219,7 @@ class ListResponse(TypedDict):
Whether there are more results available for pagination
"""

class RemoveResponse(TypedDict):
class RemoveResponse(BaseResponse):
"""
RemoveResponse is the class that wraps the response of the remove method.

Expand Down
9 changes: 5 additions & 4 deletions resend/contact_properties/_contact_properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
from typing_extensions import NotRequired, TypedDict

from resend import request
from resend._base_response import BaseResponse
from resend.pagination_helper import PaginationHelper

from ._contact_property import ContactProperty


class ContactProperties:

class CreateResponse(TypedDict):
class CreateResponse(BaseResponse):
"""
CreateResponse is the type that wraps the response of the contact property that was created.

Expand All @@ -28,7 +29,7 @@ class CreateResponse(TypedDict):
The object type, always "contact_property".
"""

class UpdateResponse(TypedDict):
class UpdateResponse(BaseResponse):
"""
UpdateResponse is the type that wraps the response of the contact property that was updated.

Expand All @@ -46,7 +47,7 @@ class UpdateResponse(TypedDict):
The object type, always "contact_property".
"""

class RemoveResponse(TypedDict):
class RemoveResponse(BaseResponse):
"""
RemoveResponse is the type that wraps the response of the contact property that was removed.

Expand Down Expand Up @@ -96,7 +97,7 @@ class ListParams(TypedDict):
Cannot be used with the after parameter.
"""

class ListResponse(TypedDict):
class ListResponse(BaseResponse):
"""
ListResponse type that wraps a list of contact property objects with pagination metadata.

Expand Down
9 changes: 5 additions & 4 deletions resend/contacts/_contacts.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from typing_extensions import NotRequired, TypedDict

from resend import request
from resend._base_response import BaseResponse
from resend.pagination_helper import PaginationHelper

from ._contact import Contact
Expand All @@ -15,7 +16,7 @@ class Contacts:
Segments = ContactSegments
Topics = Topics

class RemoveContactResponse(TypedDict):
class RemoveContactResponse(BaseResponse):
"""
RemoveContactResponse is the type that wraps the response of the contact that was removed

Expand Down Expand Up @@ -56,7 +57,7 @@ class ListParams(TypedDict):
Cannot be used with the after parameter.
"""

class ListResponse(TypedDict):
class ListResponse(BaseResponse):
"""
ListResponse type that wraps a list of contact objects with pagination metadata

Expand All @@ -79,7 +80,7 @@ class ListResponse(TypedDict):
Whether there are more results available for pagination
"""

class CreateContactResponse(TypedDict):
class CreateContactResponse(BaseResponse):
"""
CreateContactResponse is the type that wraps the response of the contact that was created

Expand All @@ -97,7 +98,7 @@ class CreateContactResponse(TypedDict):
The ID of the scheduled email that was canceled.
"""

class UpdateContactResponse(TypedDict):
class UpdateContactResponse(BaseResponse):
"""
UpdateContactResponse is the type that wraps the response of the contact that was updated

Expand Down
5 changes: 3 additions & 2 deletions resend/contacts/_topics.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from typing_extensions import NotRequired, TypedDict

from resend import request
from resend._base_response import BaseResponse
from resend.pagination_helper import PaginationHelper

from ._contact_topic import ContactTopic, TopicSubscriptionUpdate
Expand All @@ -23,7 +24,7 @@ class _ListParams(TypedDict):
"""


class _ListResponse(TypedDict):
class _ListResponse(BaseResponse):
object: str
"""
The object type: "list"
Expand Down Expand Up @@ -53,7 +54,7 @@ class _UpdateParams(TypedDict):
"""


class _UpdateResponse(TypedDict):
class _UpdateResponse(BaseResponse):
id: str
"""
The contact ID.
Expand Down
7 changes: 4 additions & 3 deletions resend/contacts/segments/_contact_segments.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from typing_extensions import NotRequired, TypedDict

from resend import request
from resend._base_response import BaseResponse
from resend.pagination_helper import PaginationHelper

from ._contact_segment import ContactSegment
Expand All @@ -14,7 +15,7 @@ class ContactSegments:
This is separate from the main Contacts API which uses audience_id.
"""

class AddContactSegmentResponse(TypedDict):
class AddContactSegmentResponse(BaseResponse):
"""
AddContactSegmentResponse is the type that wraps the response when adding a contact to a segment.

Expand All @@ -27,7 +28,7 @@ class AddContactSegmentResponse(TypedDict):
The ID of the contact segment association.
"""

class RemoveContactSegmentResponse(TypedDict):
class RemoveContactSegmentResponse(BaseResponse):
"""
RemoveContactSegmentResponse is the type that wraps the response when removing a contact from a segment.

Expand Down Expand Up @@ -72,7 +73,7 @@ class ListContactSegmentsParams(TypedDict):
Cannot be used with the after parameter.
"""

class ListContactSegmentsResponse(TypedDict):
class ListContactSegmentsResponse(BaseResponse):
"""
ListContactSegmentsResponse type that wraps a list of segment objects with pagination metadata.

Expand Down
5 changes: 3 additions & 2 deletions resend/domains/_domains.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from typing_extensions import Literal, NotRequired, TypedDict

from resend import request
from resend._base_response import BaseResponse
from resend.domains._domain import Domain
from resend.domains._record import Record
from resend.pagination_helper import PaginationHelper
Expand Down Expand Up @@ -30,7 +31,7 @@ class ListParams(TypedDict):
Cannot be used with the after parameter.
"""

class ListResponse(TypedDict):
class ListResponse(BaseResponse):
"""
ListResponse type that wraps a list of domain objects with pagination metadata

Expand All @@ -53,7 +54,7 @@ class ListResponse(TypedDict):
Whether there are more results available for pagination
"""

class CreateDomainResponse(TypedDict):
class CreateDomainResponse(BaseResponse):
"""
CreateDomainResponse is the type that wraps the response of the domain that was created

Expand Down
3 changes: 2 additions & 1 deletion resend/emails/_attachments.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from typing_extensions import NotRequired, TypedDict

from resend import request
from resend._base_response import BaseResponse
from resend.emails._received_email import (EmailAttachment,
EmailAttachmentDetails)
from resend.pagination_helper import PaginationHelper
Expand All @@ -23,7 +24,7 @@ class _ListParams(TypedDict):
"""


class _ListResponse(TypedDict):
class _ListResponse(BaseResponse):
object: str
"""
The object type: "list"
Expand Down
Loading
Loading