Skip to content

Commit 9384677

Browse files
authored
feat: Optional pagination list methods (#165)
1 parent 5f28cbc commit 9384677

17 files changed

+709
-30
lines changed

examples/api_keys.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,19 @@
2222
print(key["name"])
2323
print(key["created_at"])
2424

25+
print("\n--- Using pagination parameters ---")
26+
if keys["data"]:
27+
paginated_params: resend.ApiKeys.ListParams = {
28+
"limit": 8,
29+
"after": keys["data"][0]["id"],
30+
}
31+
paginated_keys: resend.ApiKeys.ListResponse = resend.ApiKeys.list(
32+
params=paginated_params
33+
)
34+
print(f"Retrieved {len(paginated_keys['data'])} keys with pagination")
35+
print(f"Has more keys: {paginated_keys['has_more']}")
36+
else:
37+
print("No keys available for pagination example")
38+
2539
if len(keys["data"]) > 0:
2640
resend.ApiKeys.remove(api_key_id=keys["data"][0]["id"])

examples/audiences.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,21 @@
2121

2222
audiences: resend.Audiences.ListResponse = resend.Audiences.list()
2323
print("List of audiences:", [a["id"] for a in audiences["data"]])
24+
print(f"Has more audiences: {audiences['has_more']}")
25+
26+
print("\n--- Using pagination parameters ---")
27+
if audiences["data"]:
28+
paginated_params: resend.Audiences.ListParams = {
29+
"limit": 5,
30+
"after": audiences["data"][0]["id"],
31+
}
32+
paginated_audiences: resend.Audiences.ListResponse = resend.Audiences.list(
33+
params=paginated_params
34+
)
35+
print(f"Retrieved {len(paginated_audiences['data'])} audiences with pagination")
36+
print(f"Has more audiences: {paginated_audiences['has_more']}")
37+
else:
38+
print("No audiences available for pagination example")
2439

2540
rmed: resend.Audiences.RemoveAudienceResponse = resend.Audiences.remove(
2641
id=audience["id"]

examples/broadcasts.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,19 @@
5858

5959
list_response: resend.Broadcasts.ListResponse = resend.Broadcasts.list()
6060
print("List of broadcasts !\n")
61-
print(list_response)
61+
print(f"Found {len(list_response['data'])} broadcasts")
62+
print(f"Has more broadcasts: {list_response['has_more']}")
63+
64+
print("\n--- Using pagination parameters ---")
65+
if list_response["data"]:
66+
paginated_params: resend.Broadcasts.ListParams = {
67+
"limit": 3,
68+
"after": list_response["data"][0]["id"],
69+
}
70+
paginated_broadcasts: resend.Broadcasts.ListResponse = resend.Broadcasts.list(
71+
params=paginated_params
72+
)
73+
print(f"Retrieved {len(paginated_broadcasts['data'])} broadcasts with pagination")
74+
print(f"Has more broadcasts: {paginated_broadcasts['has_more']}")
75+
else:
76+
print("No broadcasts available for pagination example")

examples/contacts.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,25 @@
4646

4747
contacts: resend.Contacts.ListResponse = resend.Contacts.list(audience_id=audience_id)
4848
print("List of contacts")
49+
print(f"Found {len(contacts['data'])} contacts")
50+
print(f"Has more contacts: {contacts['has_more']}")
4951
for c in contacts["data"]:
5052
print(c)
5153

54+
print("\n--- Using pagination parameters ---")
55+
if contacts["data"]:
56+
paginated_params: resend.Contacts.ListParams = {
57+
"limit": 2,
58+
"after": contacts["data"][0]["id"],
59+
}
60+
paginated_contacts: resend.Contacts.ListResponse = resend.Contacts.list(
61+
audience_id=audience_id, params=paginated_params
62+
)
63+
print(f"Retrieved {len(paginated_contacts['data'])} contacts with pagination")
64+
print(f"Has more contacts: {paginated_contacts['has_more']}")
65+
else:
66+
print("No contacts available for pagination example")
67+
5268
# remove by email
5369
rmed: resend.Contacts.RemoveContactResponse = resend.Contacts.remove(
5470
audience_id=audience_id, email=cont_by_email["email"]

examples/domains.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,27 @@
3333
print(f"Updated domain: {updated_domain['id']}")
3434

3535
domains: resend.Domains.ListResponse = resend.Domains.list()
36-
if not domains:
36+
print(f"Found {len(domains['data'])} domains")
37+
print(f"Has more domains: {domains['has_more']}")
38+
if not domains["data"]:
3739
print("No domains found")
3840
for domain in domains["data"]:
3941
print(domain)
4042

43+
print("\n--- Using pagination parameters ---")
44+
if domains["data"]:
45+
paginated_params: resend.Domains.ListParams = {
46+
"limit": 2,
47+
"after": domains["data"][0]["id"],
48+
}
49+
paginated_domains: resend.Domains.ListResponse = resend.Domains.list(
50+
params=paginated_params
51+
)
52+
print(f"Retrieved {len(paginated_domains['data'])} domains with pagination")
53+
print(f"Has more domains: {paginated_domains['has_more']}")
54+
else:
55+
print("No domains available for pagination example")
56+
4157
verified_domain: resend.Domain = resend.Domains.verify(domain_id=domain["id"])
4258
print(f"Verified")
4359
print(verified_domain)

resend/api_keys/_api_keys.py

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,36 @@
1-
from typing import Any, Dict, List, cast
1+
from typing import Any, Dict, List, Optional, cast
22

33
from typing_extensions import NotRequired, TypedDict
44

55
from resend import request
66
from resend.api_keys._api_key import ApiKey
7+
from resend.pagination_helper import PaginationHelper
78

89

910
class ApiKeys:
1011

1112
class ListResponse(TypedDict):
1213
"""
13-
ListResponse type that wraps a list of API key objects
14+
ListResponse type that wraps a list of API key objects with pagination metadata
1415
1516
Attributes:
17+
object (str): The object type, always "list"
1618
data (List[ApiKey]): A list of API key objects
19+
has_more (bool): Whether there are more results available
1720
"""
1821

22+
object: str
23+
"""
24+
The object type, always "list"
25+
"""
1926
data: List[ApiKey]
2027
"""
2128
A list of API key objects
2229
"""
30+
has_more: bool
31+
"""
32+
Whether there are more results available for pagination
33+
"""
2334

2435
class CreateApiKeyResponse(TypedDict):
2536
"""
@@ -39,6 +50,24 @@ class CreateApiKeyResponse(TypedDict):
3950
The token of the created API key
4051
"""
4152

53+
class ListParams(TypedDict):
54+
limit: NotRequired[int]
55+
"""
56+
Number of API keys to retrieve. Maximum is 100, and minimum is 1.
57+
"""
58+
after: NotRequired[str]
59+
"""
60+
The ID after which we'll retrieve more API keys (for pagination).
61+
This ID will not be included in the returned list.
62+
Cannot be used with the before parameter.
63+
"""
64+
before: NotRequired[str]
65+
"""
66+
The ID before which we'll retrieve more API keys (for pagination).
67+
This ID will not be included in the returned list.
68+
Cannot be used with the after parameter.
69+
"""
70+
4271
class CreateParams(TypedDict):
4372
name: str
4473
"""
@@ -75,15 +104,23 @@ def create(cls, params: CreateParams) -> CreateApiKeyResponse:
75104
return resp
76105

77106
@classmethod
78-
def list(cls) -> ListResponse:
107+
def list(cls, params: Optional[ListParams] = None) -> ListResponse:
79108
"""
80109
Retrieve a list of API keys for the authenticated user.
81110
see more: https://resend.com/docs/api-reference/api-keys/list-api-keys
82111
112+
Args:
113+
params (Optional[ListParams]): Optional pagination parameters
114+
- limit: Number of API keys to retrieve (max 100, min 1)
115+
- after: ID after which to retrieve more API keys
116+
- before: ID before which to retrieve more API keys
117+
83118
Returns:
84119
ListResponse: A list of API key objects
85120
"""
86-
path = "/api-keys"
121+
base_path = "/api-keys"
122+
query_params = cast(Dict[Any, Any], params) if params else None
123+
path = PaginationHelper.build_paginated_path(base_path, query_params)
87124
resp = request.Request[ApiKeys.ListResponse](
88125
path=path, params={}, verb="get"
89126
).perform_with_content()

resend/audiences/_audiences.py

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
from typing import Any, Dict, List, cast
1+
from typing import Any, Dict, List, Optional, cast
22

3-
from typing_extensions import TypedDict
3+
from typing_extensions import NotRequired, TypedDict
44

55
from resend import request
6+
from resend.pagination_helper import PaginationHelper
67

78
from ._audience import Audience
89

@@ -32,23 +33,46 @@ class RemoveAudienceResponse(TypedDict):
3233
Whether the audience was deleted
3334
"""
3435

36+
class ListParams(TypedDict):
37+
limit: NotRequired[int]
38+
"""
39+
Number of audiences to retrieve. Maximum is 100, and minimum is 1.
40+
"""
41+
after: NotRequired[str]
42+
"""
43+
The ID after which we'll retrieve more audiences (for pagination).
44+
This ID will not be included in the returned list.
45+
Cannot be used with the before parameter.
46+
"""
47+
before: NotRequired[str]
48+
"""
49+
The ID before which we'll retrieve more audiences (for pagination).
50+
This ID will not be included in the returned list.
51+
Cannot be used with the after parameter.
52+
"""
53+
3554
class ListResponse(TypedDict):
3655
"""
37-
ListResponse type that wraps a list of audience objects
56+
ListResponse type that wraps a list of audience objects with pagination metadata
3857
3958
Attributes:
40-
object (str): The object type, "list"
59+
object (str): The object type, always "list"
4160
data (List[Audience]): A list of audience objects
61+
has_more (bool): Whether there are more results available
4262
"""
4363

4464
object: str
4565
"""
46-
The object type, "list"
66+
The object type, always "list"
4767
"""
4868
data: List[Audience]
4969
"""
5070
A list of audience objects
5171
"""
72+
has_more: bool
73+
"""
74+
Whether there are more results available for pagination
75+
"""
5276

5377
class CreateAudienceResponse(TypedDict):
5478
"""
@@ -99,15 +123,24 @@ def create(cls, params: CreateParams) -> CreateAudienceResponse:
99123
return resp
100124

101125
@classmethod
102-
def list(cls) -> ListResponse:
126+
def list(cls, params: Optional[ListParams] = None) -> ListResponse:
103127
"""
104128
Retrieve a list of audiences.
105129
see more: https://resend.com/docs/api-reference/audiences/list-audiences
106130
131+
Args:
132+
params (Optional[ListParams]): Optional pagination parameters
133+
- limit: Number of audiences to retrieve (max 100, min 1).
134+
If not provided, all audiences will be returned without pagination.
135+
- after: ID after which to retrieve more audiences
136+
- before: ID before which to retrieve more audiences
137+
107138
Returns:
108139
ListResponse: A list of audience objects
109140
"""
110-
path = "/audiences/"
141+
base_path = "/audiences"
142+
query_params = cast(Dict[Any, Any], params) if params else None
143+
path = PaginationHelper.build_paginated_path(base_path, query_params)
111144
resp = request.Request[Audiences.ListResponse](
112145
path=path, params={}, verb="get"
113146
).perform_with_content()

resend/broadcasts/_broadcasts.py

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
from typing import Any, Dict, List, Union, cast
1+
from typing import Any, Dict, List, Optional, Union, cast
22

33
from typing_extensions import NotRequired, TypedDict
44

55
from resend import request
6+
from resend.pagination_helper import PaginationHelper
67

78
from ._broadcast import Broadcast
89

@@ -160,23 +161,46 @@ class SendResponse(CreateResponse):
160161
id (str): id of the created broadcast
161162
"""
162163

164+
class ListParams(TypedDict):
165+
limit: NotRequired[int]
166+
"""
167+
Number of broadcasts to retrieve. Maximum is 100, and minimum is 1.
168+
"""
169+
after: NotRequired[str]
170+
"""
171+
The ID after which we'll retrieve more broadcasts (for pagination).
172+
This ID will not be included in the returned list.
173+
Cannot be used with the before parameter.
174+
"""
175+
before: NotRequired[str]
176+
"""
177+
The ID before which we'll retrieve more broadcasts (for pagination).
178+
This ID will not be included in the returned list.
179+
Cannot be used with the after parameter.
180+
"""
181+
163182
class ListResponse(TypedDict):
164183
"""
165-
ListResponse is the class that wraps the response of the list method.
184+
ListResponse is the class that wraps the response of the list method with pagination metadata.
166185
167186
Attributes:
168-
object (str): object type: "list"
187+
object (str): object type, always "list"
169188
data (List[Broadcast]): A list of broadcast objects
189+
has_more (bool): Whether there are more results available
170190
"""
171191

172192
object: str
173193
"""
174-
object type: "list"
194+
object type, always "list"
175195
"""
176196
data: List[Broadcast]
177197
"""
178198
A list of broadcast objects
179199
"""
200+
has_more: bool
201+
"""
202+
Whether there are more results available for pagination
203+
"""
180204

181205
class RemoveResponse(TypedDict):
182206
"""
@@ -259,15 +283,24 @@ def send(cls, params: SendParams) -> SendResponse:
259283
return resp
260284

261285
@classmethod
262-
def list(cls) -> ListResponse:
286+
def list(cls, params: Optional[ListParams] = None) -> ListResponse:
263287
"""
264288
Retrieve a list of broadcasts.
265289
see more: https://resend.com/docs/api-reference/broadcasts/list-broadcasts
266290
291+
Args:
292+
params (Optional[ListParams]): Optional pagination parameters
293+
- limit: Number of broadcasts to retrieve (max 100, min 1).
294+
If not provided, all broadcasts will be returned without pagination.
295+
- after: ID after which to retrieve more broadcasts
296+
- before: ID before which to retrieve more broadcasts
297+
267298
Returns:
268299
ListResponse: A list of broadcast objects
269300
"""
270-
path = "/broadcasts/"
301+
base_path = "/broadcasts"
302+
query_params = cast(Dict[Any, Any], params) if params else None
303+
path = PaginationHelper.build_paginated_path(base_path, query_params)
271304
resp = request.Request[Broadcasts.ListResponse](
272305
path=path, params={}, verb="get"
273306
).perform_with_content()

0 commit comments

Comments
 (0)