A Composio call asks for N results — say max_results=10. The tool returns 10 messages and a nextPageToken. To get all messages, you keep calling, passing the token each time, until the token is empty.
The pattern is a while loop with the page token as the loop variable:
all_messages = []
page_token = None
while True:
args = {"max_results": 10}
if page_token:
args["page_token"] = page_token
result = toolset.execute_action(Action.GMAIL_FETCH_EMAILS, args)
page = result.get("messages", [])
all_messages.extend(page)
page_token = result.get("nextPageToken")
if not page_token:
break
print(f"got {len(all_messages)} total messages")What stops it from looping forever?
Two things. (1) The tool eventually returns no nextPageToken (you've consumed all pages) — break. (2) For safety, cap the iteration count yourself in production scripts:
for _ in range(50): # at most 50 pages × 10 = 500 messages
...
if not page_token:
breakProtects against bugs where the token never goes empty.
Some APIs use offsets instead of tokens, right?
Yes — ?offset=0&limit=10, ?offset=10&limit=10, etc. Same pattern, different variable. Loop until you get back fewer than limit items, or until offset exceeds a known total.
Many APIs return a page of results plus a token to fetch the next page. The loop:
all_items = []
page_token = None
while True:
args = {"max_results": 10}
if page_token:
args["page_token"] = page_token
result = SOME_CALL(args)
page = result.get("items", [])
all_items.extend(page)
page_token = result.get("nextPageToken")
if not page_token:
break| Style | Tool | How |
|---|---|---|
| Token | Gmail, Drive, Notion | Pass page_token from prev response |
| Offset/limit | Many REST APIs | Pass ?offset=N&limit=M until short page |
| Cursor | Some GraphQL APIs | Pass after cursor |
| Page number | Older APIs | Pass page=1, page=2, ... |
Composio normalises some of this for you — check the action docs to see which arg names apply.
MAX_PAGES = 100
for _ in range(MAX_PAGES):
...
if not page_token:
break
else:
print(f"warning: hit MAX_PAGES — there may be more results")Defence against bugs where the token never empties. The else runs only if no break happened — useful diagnostic.
If you're searching for a specific item, break as soon as you find it. No need to fetch every page.
for page in pages:
for msg in page:
if msg["subject"] == target:
return msg # found it
# falls off the end → not foundA Composio call asks for N results — say max_results=10. The tool returns 10 messages and a nextPageToken. To get all messages, you keep calling, passing the token each time, until the token is empty.
The pattern is a while loop with the page token as the loop variable:
all_messages = []
page_token = None
while True:
args = {"max_results": 10}
if page_token:
args["page_token"] = page_token
result = toolset.execute_action(Action.GMAIL_FETCH_EMAILS, args)
page = result.get("messages", [])
all_messages.extend(page)
page_token = result.get("nextPageToken")
if not page_token:
break
print(f"got {len(all_messages)} total messages")What stops it from looping forever?
Two things. (1) The tool eventually returns no nextPageToken (you've consumed all pages) — break. (2) For safety, cap the iteration count yourself in production scripts:
for _ in range(50): # at most 50 pages × 10 = 500 messages
...
if not page_token:
breakProtects against bugs where the token never goes empty.
Some APIs use offsets instead of tokens, right?
Yes — ?offset=0&limit=10, ?offset=10&limit=10, etc. Same pattern, different variable. Loop until you get back fewer than limit items, or until offset exceeds a known total.
Many APIs return a page of results plus a token to fetch the next page. The loop:
all_items = []
page_token = None
while True:
args = {"max_results": 10}
if page_token:
args["page_token"] = page_token
result = SOME_CALL(args)
page = result.get("items", [])
all_items.extend(page)
page_token = result.get("nextPageToken")
if not page_token:
break| Style | Tool | How |
|---|---|---|
| Token | Gmail, Drive, Notion | Pass page_token from prev response |
| Offset/limit | Many REST APIs | Pass ?offset=N&limit=M until short page |
| Cursor | Some GraphQL APIs | Pass after cursor |
| Page number | Older APIs | Pass page=1, page=2, ... |
Composio normalises some of this for you — check the action docs to see which arg names apply.
MAX_PAGES = 100
for _ in range(MAX_PAGES):
...
if not page_token:
break
else:
print(f"warning: hit MAX_PAGES — there may be more results")Defence against bugs where the token never empties. The else runs only if no break happened — useful diagnostic.
If you're searching for a specific item, break as soon as you find it. No need to fetch every page.
for page in pages:
for msg in page:
if msg["subject"] == target:
return msg # found it
# falls off the end → not foundCreate a free account to get started. Paid plans unlock all tracks.