Skip to content
Skip to main content

How to get unread email counts

Last updated:

You want a badge on your app’s inbox icon that shows how many unread messages are waiting, the same number a user sees in Gmail or Outlook. There are two ways to get it: read the count a provider already keeps per folder, or count unread messages yourself with a filter.

The Folders API returns an unread_count on each folder, so a single request gives you every folder’s badge number at once. When you need a count scoped to a query the provider doesn’t pre-aggregate, you filter messages with unread=true and tally the results. This recipe covers both.

How do I get the unread count for a folder?

Section titled “How do I get the unread count for a folder?”

Read the unread_count field on a folder object. A GET /v3/grants/{grant_id}/folders request returns every folder with an unread_count (unread items) and a total_count (all items), both read-only integers the provider maintains. One call covers all folders, so you get the inbox badge and every other folder’s number without a separate request per folder.

The request below lists all folders for a grant. Find the folder whose attributes array contains \Inbox, then read its unread_count for the inbox badge. The same response returns counts for the Sent, Trash, and any custom folders in one round trip across all 6 providers.

Match on the \Inbox attribute rather than a folder named “INBOX”, because providers localize and rename folders. See Organize email with folders and labels for the full attribute list.

How exact the number is depends on the provider. Microsoft and EWS model mail as single-parent folders, where each message lives in exactly one folder and the unread_count reflects that folder’s contents directly. Gmail models folders as labels instead, so the same message can carry both the INBOX and UNREAD labels at once, and reading the UNREAD label’s count gives you the mailbox-wide unread total rather than a per-folder figure. Treat the folder count as the provider’s own current number, not a value Nylas recomputes.

The unread_count always travels with a total_count, which is the number of all items in the folder, both read and unread. Both are read-only integers you can’t write back, since the provider owns them. The folder object also carries child_count, the number of immediate sub-folders nested under it, but that field is Microsoft and EWS only because Gmail’s flat label model has no nested folders to count. Two more side effects of the label model are worth planning for: a Gmail message that sits in two labels shows up in both folders’ item lists, so summing every folder’s total_count over-counts a Gmail mailbox, and there’s no “Archive” folder count to read because archiving in Gmail just removes the INBOX label.

How do I count unread messages with a filter?

Section titled “How do I count unread messages with a filter?”

Count unread messages by listing them with unread=true. A GET /v3/grants/{grant_id}/messages?unread=true request returns only unread messages, so the size of the result set is your count. This is the approach to reach for when you need a count the folder object doesn’t track, such as unread messages received in the last 24 hours or unread mail in a specific label.

The request below returns unread messages, capped at the default page size of 50 and a maximum of 200 per page. To scope the count to one folder, add the in parameter with a folder id from the folders list. The unread filter works the same way across Google, Microsoft, Yahoo, iCloud, IMAP, and EWS.

You’d reach for this filter over the folder unread_count whenever the count needs a condition the folder object can’t express. Combine unread=true with received_after (a Unix timestamp in seconds) for “unread mail from the last 24 hours”, or with from to count unread mail from one sender. The folder field can’t answer those, because the provider only pre-aggregates a single number per folder. The trade is request cost: the folder number is one call, while filtering walks the result set 200 messages at a time.

For an exact total above 200, you page through the cursor and sum each page, covered next.

The Nylas CLI gives you the unread set from your terminal: nylas email list --unread returns only unread messages, which you can pipe into jq or a script to produce a badge count.

# List unread messages
nylas email list --unread
# Emit unread messages as JSON to pipe into a counter
nylas email list --unread --json

For a true count you page through results, since email list returns 10 messages by default and paginates over 200. Scope the count to a sender or folder with --from or --folder. See the email list command reference for the filter flags.

What’s the difference between inbox unread and all unread?

Section titled “What’s the difference between inbox unread and all unread?”

Inbox unread counts only messages in the inbox folder, while all unread counts every unread message regardless of folder, including Sent, Archive, and custom folders. Scope a count to the inbox by adding in=<folderId> with the inbox folder’s id from the folders list; omit in to count unread mail across the whole mailbox.

Most badges show inbox unread, since that’s the number a user recognizes from Gmail or Outlook. A GET /v3/grants/{grant_id}/messages?unread=true&in=<folderId> request returns only unread messages in that one folder, so its result count is the inbox badge. Drop the in parameter and the same request counts unread mail everywhere, which is useful for a “you have unread mail somewhere” indicator. The folder unread_count already gives you the per-folder inbox number for free, so reach for the in-scoped filter only when you also need a date or search condition the folder object doesn’t track, such as unread mail in the inbox received in the last 7 days.

Count unread messages across more than one page

Section titled “Count unread messages across more than one page”

A single list response holds at most 200 messages, so a mailbox with more unread mail than that needs pagination to reach an exact total. Each GET /v3/grants/{grant_id}/messages?unread=true response includes a next_cursor. You request the next page with page_token set to that cursor, add each page’s length to a running total, and stop when next_cursor comes back empty.

The loop below sums unread messages page by page. The page size matters a lot here: the default limit is 50, so a 5,000-unread inbox needs 100 pages, while setting limit=200 (the maximum) cuts the same mailbox to 25 requests. Always set limit=200 for counting work. If you only need to show “99+” in a badge, stop after the first page instead of paging the whole mailbox, since most UIs cap the displayed number anyway.

Prefer the folder unread_count for a plain inbox badge; reach for this loop only when you need a count the folder object doesn’t already track.

Keep an unread badge in sync by caching the count and updating it from webhooks instead of re-counting on a timer. Store the current number, subscribe to message and folder events, and adjust the cached value each time a notification arrives. Polling every few seconds wastes requests and still lags behind the real mailbox.

Subscribe to four triggers that change the unread total: message.created (new mail, so increment), message.updated (a read or unread flag flipped, so re-read the message’s unread field and adjust), message.deleted (an unread message removed, so decrement), and folder.updated (the provider recomputed a folder’s unread_count, so trust the new number). Each Nylas webhook payload is at most 1 MB; larger ones arrive with a .truncated suffix and you re-query the affected object by ID. Reconcile against the folder unread_count on a slow schedule, such as once an hour or on app focus, so a missed notification can’t leave the badge permanently wrong. This event-driven cache cuts a hot mailbox from one count per page refresh down to one write per actual change. See Get real-time updates with webhooks for the full trigger list and payload format.

The fastest path is the folder unread_count, since one folders request returns the unread and total numbers for every folder. The filter-and-count approach is for cases the provider doesn’t pre-aggregate, like unread mail newer than a timestamp, and it costs one request per 200 messages instead of one request for the whole mailbox.

Provider coverage differs. The unread_count and total_count fields come back across providers, but child_count (nested sub-folders) is Microsoft and EWS only, per the Folders API reference. Gmail models folders as labels, so a message can carry the UNREAD label alongside INBOX, and its unread state lives on the label rather than a single folder. Don’t assume a one-to-one folder-to-count mapping on Gmail.

Counting a large mailbox is expensive. A filter count pages at 200 messages each, so a 50,000-message unread backlog is 250 requests. Use the folder unread_count for totals, and reserve the filter loop for narrow queries. If you hit 429 errors when listing, the Messages API reference recommends dropping limit to 20 and adding query parameters to narrow the result set. Rate limits apply per grant: Google throttles per user and per project, Microsoft per mailbox.

The unread flag is part of the message, so you can move it. The unread boolean on each message is writable through an update request, which is what flips a folder’s unread_count when a user reads or re-flags mail. To change read state from your own app rather than just reading it, see Mark messages read, unread, or starred.