# Email sending limits for scripts: Gmail, Workspace, Microsoft 365

> Gmail stops at 500 a day, Workspace at 2,000, Microsoft 365 at 10,000 recipients and 30 a minute. The limits, the error codes and a paced sender.

- Published: 2026-11-01
- Author: 28creation Desk
- Format: Guide
- URL: https://28creation.com/articles/email-sending-limits-scripts/

## The short read

**Microsoft 365 reports a full daily quota with code 5.1.90, which a script can mistake for one bad address.**

- Gmail allows 500 emails a day, Workspace 2,000 messages and 2,000 unique outside addresses, Microsoft 365 10,000 recipients and 30 messages a minute per mailbox.
- Our Python sender paced, stopped and resumed without duplicates in 7 tests on a local server with 25 invented rows, not a real provider.

### What you can do with this

> Before a script mails a list, set its daily cap below your account's smallest limit and treat only 5.1.1 and 5.1.10 as bad addresses. Anything else should stop the run.

---

A script that emails a list from an ordinary mailbox hits limits the mail client never shows. Personal Gmail stops at [500 emails a day](https://support.google.com/mail/answer/22839?hl=en). A Google Workspace user stops at [2,000 messages and 2,000 unique outside addresses](https://knowledge.workspace.google.com/admin/gmail/gmail-sending-limits-in-google-workspace?hl=en), then can't send for up to 24 hours. A Microsoft 365 mailbox allows [10,000 recipients a day and 30 messages a minute](https://learn.microsoft.com/en-us/office365/servicedescriptions/exchange-online-service-description/exchange-online-limits), plus a company-wide cap on outside recipients.

Hit one of these and the mailbox stops for everyone who uses it, not only the script.

This guide puts the limits for each route in one table and explains how each is counted. It lists the error codes each provider returns and gives a Python sender that stays under them. We read every limit on October 27, 2026, and tested the script against a local SMTP server we set up to act like a provider at its limit.

## The limits by route, read October 27, 2026

How you send decides which limit applies. The same Workspace user has one daily limit in Gmail, a lower per-message cap over SMTP, and a much higher one through Google's SMTP relay.

| Route | Per day | Per message | Per minute | Source |
|---|---|---|---|---|
| Personal Gmail | 500 emails; one email to more than 500 recipients triggers the block | 500 recipients | Not stated | [Gmail Help](https://support.google.com/mail/answer/22839?hl=en) |
| Workspace, Gmail or SMTP | 2,000 messages; 10,000 total recipients; 3,000 unique recipients, of which 2,000 external | 2,000 in Gmail (500 external); 100 over SMTP | Not stated | [Workspace limits](https://knowledge.workspace.google.com/admin/gmail/gmail-sending-limits-in-google-workspace?hl=en) |
| Workspace, Gmail API | The Workspace daily limits, which the API page links to | 500 recipients | 60 sends per user, by quota units | [Gmail API quota](https://developers.google.com/workspace/gmail/api/reference/quota) |
| Workspace trial | 500 messages; 500 unique external recipients | As above | Not stated | [Workspace limits](https://knowledge.workspace.google.com/admin/gmail/gmail-sending-limits-in-google-workspace?hl=en) |
| Workspace SMTP relay | 10,000 unique recipients and 10,000 messages per user | 100 per SMTP transaction | 319,444 recipients per 10 minutes for the whole organization | [SMTP relay](https://knowledge.workspace.google.com/admin/gmail/advanced/route-outgoing-smtp-relay-messages-through-google?hl=en) |
| Apps Script `MailApp` | 100 recipients (personal), 1,500 (Workspace) | 50 recipients | Not stated | [Apps Script quotas](https://developers.google.com/apps-script/guides/services/quotas) |
| Microsoft 365 mailbox, every plan listed | 10,000 recipients | Up to 1,000 recipients, set by the admin | 30 messages | [Exchange Online limits](https://learn.microsoft.com/en-us/office365/servicedescriptions/exchange-online-service-description/exchange-online-limits) |
| Microsoft 365 tenant, outside recipients | Depends on licenses (next section) | | | [Exchange Online limits](https://learn.microsoft.com/en-us/office365/servicedescriptions/exchange-online-service-description/exchange-online-limits) |
| Microsoft 365, from an onmicrosoft.com address | 100 outside recipients per tenant | | | [Exchange Online limits](https://learn.microsoft.com/en-us/office365/servicedescriptions/exchange-online-service-description/exchange-online-limits) |

One cell is our arithmetic, not a number the vendor prints. The Gmail API gives each user 6,000 quota units a minute and charges 100 units for `messages.send`. That caps one user at 60 sends a minute. The Workspace page was last updated October 22, 2026, the Apps Script page September 3, 2026, the Gmail API page October 16, 2026, and Microsoft's page October 19, 2026.

The SMTP relay row needs an admin with the Gmail Settings privilege to set it up in the Admin console. Google [lowers relay limits](https://knowledge.workspace.google.com/admin/gmail/advanced/route-outgoing-smtp-relay-messages-through-google?hl=en) for trial accounts and for accounts with an unpaid bill. Workspace's ordinary limits also rise on their own once a domain has paid at least $100 in total, which Google says can take up to 75 days to show.

## Three ways the counting goes wrong

The numbers above look comparable. They are counted three different ways, and each can stop a script that looked safely under the headline figure.

**Rolling window or fixed reset.** Workspace limits apply "over a rolling 24-hour period, not a set time of day."

Microsoft counts the same way. In its example, a user who sends to 5,000 recipients at 09:00, 2,500 at 10:00 and 2,500 at 11:00 has used all 10,000 and "won't be able to send messages again until 09:00 AM the next day." Apps Script is different again: its quotas "reset 24 hours after the first request." So a script that waits for midnight UTC gains nothing on any of them.

**Messages, recipients or unique recipients.** Google counts recipients each time: "5 messages sent to 10 addresses count as 50 total recipients." Its unique-recipient limit counts each address once a day.

Microsoft counts a distribution group from the shared address book as one recipient for the mailbox limit. Its tenant-wide external limit counts "distribution group members individually." So one message to a group with 1,000 outside members uses 1,000 of the tenant's allowance.

**Per user or per company.** Most limits belong to one sender. Microsoft's external recipient limit belongs to the tenant, so a script on one mailbox can stop the sales team's outside mail. Google counts mail from a user's aliases, from delegates and from the vacation responder against that user.

## Microsoft 365's tenant limit is shared by every mailbox

Microsoft calls it the Tenant External Recipient Rate Limit, or TERRL: "the maximum number of external recipients a tenant can send to per day (24-hour sliding window)." It depends on the number of licenses. Microsoft's limits page names the report that shows it: Reports, then Mail flow, then **Tenant Outbound External Recipients** in the Exchange admin center.

The formula itself is in Microsoft's message center post [MC1454397](https://mc.merill.net/message/MC1454397) (published August 14, 2026, updated September 29, 2026, read through a public mirror of the message center). For paid tenants the existing formula "remains unchanged: 500 × (Purchased Email Licenses^0.7) + 9500." We worked it out for common tenant sizes:

| Licenses | Outside recipients per 24 hours | First 30 days of a new tenant (10%) | Days 31 to 60 (25%) |
|---|---|---|---|
| 1 | about 10,000 | about 1,000 | about 2,500 |
| 10 | about 12,006 | about 1,201 | about 3,001 |
| 25 | about 14,259 | about 1,426 | about 3,565 |
| 50 | about 17,231 | about 1,723 | about 4,308 |
| 100 | about 22,059 | about 2,206 | about 5,515 |

Microsoft doesn't say how it rounds, so treat the last digit loosely.

The same post cuts new tenants to 10% of that figure in their first 30 days and 25% from day 31 to 60. Trial tenants drop from 5,000 to 500 outside recipients a day. That rollout runs from mid-September to the end of November 2026, while Microsoft's limits page, updated October 19, 2026, still gives trial tenants 5,000. Check the admin center report rather than either figure.

Take a 10-license tenant. A script that spends its mailbox's full 10,000 on outside addresses leaves the other nine people about 2,006 for the rest of the 24 hours. On a two-week-old tenant the whole company has about 1,201, far below any one mailbox's limit.

## What hitting a limit looks like

Scripts see these as SMTP replies or, for Microsoft, as non-delivery reports. The codes below are from [Google's SMTP error list](https://knowledge.workspace.google.com/admin/support/troubleshooting/gmail-smtp-errors-and-codes?hl=en) (updated October 22, 2026) and [Microsoft's NDR reference](https://learn.microsoft.com/en-us/troubleshoot/exchange/email-delivery/ndr/non-delivery-reports-in-exchange-online) (updated August 24, 2026).

| Code | Provider | Meaning | What the script should do |
|---|---|---|---|
| 550 5.4.5 | Google | "Daily user sending limit exceeded" or "Daily SMTP relay limit exceeded for user" | Stop |
| 550 5.5.3 | Google | "Too many recipients for this sender" | Stop |
| 452 4.5.3 | Google | "Your message has too many recipients" | Send to fewer recipients per message |
| 550 5.7.1 | Google | Includes "Daily SMTP relay sending limit exceeded for this customer" | Stop |
| 5.1.90 | Microsoft | "you've reached your daily limit for message recipients" | Stop |
| 5.2.2 | Microsoft | "Submission quota exceeded" | Stop |
| 5.7.233 | Microsoft | The tenant exceeded its daily limit for outside recipients (5.7.232 on a trial tenant) | Stop; it blocks the whole tenant |
| 5.7.236 | Microsoft | Over 100 outside recipients from onmicrosoft.com addresses | Stop and send from your own domain |
| 550 5.1.1 | Both | Google: "The email account that you tried to reach does not exist"; Microsoft: "Bad destination mailbox address" | Skip that row and go on |
| 5.1.10 | Microsoft | "Recipient not found" | Skip that row and go on |

Look at 5.1.90. A tempting shortcut in a script treats any code that starts with 5.1 as a bad address, logs the row as failed and moves on.

Microsoft uses 5.1.90 for a full daily quota, and 5.1.0 means "Sender denied." A script with the prefix rule keeps going through the list and marks every remaining person as undeliverable. Our test below shows how many rows that loses.

Microsoft's per-minute limit is softer. Its page says excess messages "will be throttled and successively carried over to the following minutes." That "will typically not block the sender's account."

The same page says messages sent faster over SMTP client submission "will be rejected and the client will need to retry." Pacing below 30 a minute avoids depending on either.

## A sender that paces, stops and resumes

The script below sends one message per row of a CSV file over SMTP. It uses only Python's standard library. Four rules guard against the failures above:

- It waits `60 / per-minute` seconds between messages, 3 seconds at the default of 20.
- It logs each recipient with the UTC time and counts the last 24 hours before sending, so its window rolls the way Google and Microsoft count.
- It skips a row only on 5.1.1 or 5.1.10. Any other refusal, any 4xx reply and a dropped connection stop the run with exit code 3.
- It writes each log line, and forces it to disk, right after the server accepts the message, so a second run skips everyone already done.
- It locks the log on Linux and macOS, so a second copy started while the first is running stops instead of sending the same rows. On Windows it warns that it can't.

```python
#!/usr/bin/env python3
"""Send one email per CSV row without running into the provider's sending limits.

Paces messages, counts a rolling 24 hours from a log file, stops on a limit
error, and skips rows already handled when you run it again. Standard library
only, Python 3.9 or later.
"""
import argparse, csv, json, os, smtplib, ssl, sys, time
from datetime import datetime, timedelta, timezone
from email.message import EmailMessage
from email.utils import make_msgid
from string import Template

# Codes that are about one bad address, so the run may go on. Everything else
# stops it, including Microsoft's 5.1.90 (daily recipient limit), which shares
# the 5.1 prefix, and 5.1.0 (sender denied).
SKIP_ONE = ("5.1.1", "5.1.10")


def now():
    return datetime.now(timezone.utc)


def read_log(path):
    done, times = set(), []
    if not os.path.exists(path):
        return done, times
    if os.path.getsize(path):
        with open(path, "rb+") as f:  # a crash can leave half a line; start the next on its own
            f.seek(-1, 2)
            if f.read(1) != b"\n":
                f.write(b"\n")
    with open(path, encoding="utf-8") as f:
        for n, line in enumerate(f, 1):
            try:
                e = json.loads(line)
                done.add(e["to"].lower())
                times.append(datetime.fromisoformat(e["time"]))
            except (ValueError, KeyError):
                print(f"log line {n} unreadable, ignored", file=sys.stderr)
    return done, times


def append_log(path, to, status, detail=""):
    with open(path, "a", encoding="utf-8") as f:
        f.write(json.dumps({"time": now().isoformat(), "to": to,
                            "status": status, "detail": detail}) + "\n")
        f.flush()
        os.fsync(f.fileno())


def enhanced_code(text):
    parts = text.split()
    return parts[0] if parts and parts[0].count(".") == 2 else ""


def main():
    p = argparse.ArgumentParser()
    p.add_argument("csv")
    p.add_argument("--log", default="sent-log.jsonl")
    p.add_argument("--daily-cap", type=int, required=True,
                   help="most recipients in any 24 hours, below your provider's limit")
    p.add_argument("--per-minute", type=float, default=20)
    p.add_argument("--host", required=True)
    p.add_argument("--port", type=int, default=587)
    p.add_argument("--tls", choices=["starttls", "ssl", "none"], default="starttls")
    p.add_argument("--user", help="password comes from the SMTP_PASSWORD variable")
    p.add_argument("--sender", required=True)
    p.add_argument("--subject", required=True)
    p.add_argument("--body", required=True, help="text file; $name and other CSV columns are filled in")
    p.add_argument("--dry-run", action="store_true")
    a = p.parse_args()
    if a.daily_cap < 1 or a.per_minute <= 0:
        sys.exit("--daily-cap and --per-minute must be above 0")

    with open(a.body, encoding="utf-8") as f:
        body = Template(f.read())
    with open(a.csv, newline="", encoding="utf-8-sig") as f:
        rows = list(csv.DictReader(f))
    if rows and "email" not in rows[0]:
        sys.exit("the CSV needs an 'email' column")

    lock = open(a.log + ".lock", "w")
    try:  # two runs at once would send the same rows twice
        import fcntl
        fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB)
    except ImportError:
        print("no file locking on this system; never start two runs at once", file=sys.stderr)
    except OSError:
        sys.exit("another run is using this log; stopping")

    done, times = read_log(a.log)
    todo, seen = [], set(done)
    for r in rows:
        to = (r.get("email") or "").strip()
        if "@" not in to:
            print(f"skipped, not an address: {to!r}")
        elif to.lower() not in seen:
            seen.add(to.lower())
            todo.append((to, r))
    window = [t for t in times if t > now() - timedelta(hours=24)]
    room = a.daily_cap - len(window)
    print(f"{len(todo)} to send, {len(done)} already in the log, "
          f"{len(window)} in the last 24 hours, room for {max(room, 0)}")
    if not todo:
        return 0
    if a.dry_run:
        for to, _ in todo[:max(room, 0)]:
            print("would send to", to)
        return 0

    server = None
    if room > 0:
        if a.tls == "ssl":
            server = smtplib.SMTP_SSL(a.host, a.port, context=ssl.create_default_context(), timeout=60)
        else:
            server = smtplib.SMTP(a.host, a.port, timeout=60)
            if a.tls == "starttls":
                server.starttls(context=ssl.create_default_context())
        if a.user:
            server.login(a.user, os.environ["SMTP_PASSWORD"])

    gap, last, sent, code = 60 / a.per_minute, 0.0, 0, 0
    for to, row in todo:
        if room <= 0:
            window.sort()
            free = window[len(window) - a.daily_cap] + timedelta(hours=24)
            print(f"daily cap reached; next room at {free:%Y-%m-%d %H:%M} UTC. Run again then.")
            code = 3
            break
        wait = last + gap - time.monotonic()
        if wait > 0:
            time.sleep(wait)
        msg = EmailMessage()
        msg["From"], msg["To"], msg["Subject"] = a.sender, to, a.subject
        msg["Message-ID"] = make_msgid()
        msg.set_content(body.safe_substitute(row))
        last = time.monotonic()
        try:
            server.send_message(msg)
        except smtplib.SMTPRecipientsRefused as e:
            c, text = e.recipients[to]
            text = text.decode(errors="replace")
            if c >= 500 and enhanced_code(text) in SKIP_ONE:
                append_log(a.log, to, "rejected", f"{c} {text}")
                window.append(now()); room -= 1
                print(f"rejected {to}: {c} {text}")
                continue
            print(f"stopped at {to}: {c} {text}")
            code = 3
            break
        except smtplib.SMTPResponseException as e:
            print(f"stopped at {to}: {e.smtp_code} {e.smtp_error.decode(errors='replace')}")
            code = 3
            break
        except (smtplib.SMTPServerDisconnected, OSError) as e:
            print(f"connection lost at {to}: {e}. Check whether it arrived before running again.")
            code = 3
            break
        append_log(a.log, to, "sent")
        window.append(now()); room -= 1; sent += 1
    if server:
        try:
            server.quit()
        except smtplib.SMTPException:
            pass
    print(f"sent {sent} this run")
    return code


if __name__ == "__main__":
    sys.exit(main())
```

Save it as `send_paced.py`. The CSV needs an `email` column; every other column can appear in the body as `$name`, `$invoice` and so on. Run it with `--dry-run` first to see the count and who would get mail:

```bash
export SMTP_PASSWORD='...'
python3 send_paced.py customers.csv --host smtp.example.com --user you@example.com \
  --sender you@example.com --subject "Your October invoice" --body body.txt \
  --daily-cap 1500 --per-minute 20 --dry-run
```

Remove `--dry-run` to send. When it prints "daily cap reached", it also prints the UTC time when room opens; run the same command again then. A scheduler can run it every hour: a run with nothing to do sends nothing, and the lock stops a run that starts while the last one is still going.

Rejected addresses use room in the window too. We don't know whether Google or Microsoft count a refused recipient, so the script assumes they do.

## What our test on a local SMTP server showed

We had no Gmail or Microsoft 365 account to send from. So we ran the script on Python 3.11 against a local SMTP server built with `aiosmtpd` 1.4.6.

The server accepted mail until a set number of recipients, then answered every recipient with the limit reply we chose. The list was 25 invented rows. It held 22 valid `example.com` addresses, one of them repeated in capitals, a row with "n/a" for an address, and one address our server rejected with 5.1.1.

| Test | What happened |
|---|---|
| Full run at 30 a minute | 22 delivered, 1 rejected and logged, "n/a" and the repeat skipped; 44.2 seconds, every message 2.00 seconds after the last |
| Same command again | 0 sent: all 23 addresses were in the log |
| Server answers 550 5.4.5 after 10 | Stopped at row 11 with exit code 3; a second run against a fresh server sent the other 12; 22 delivered, no address twice |
| Server answers 5.1.90 after 5 | Stopped at row 6; nothing marked as rejected |
| Process killed with `kill -9` after 5 seconds | Log and server both had 11; the next run sent the other 11, no duplicates |
| Cap of 10, log holding 8 sends from 23 hours 50 minutes ago | Sent 2, stopped, and named the time 10 minutes later when room opens |
| Second run started 1 second after the first, same log | Second run refused to start; the first sent all 22, none twice |

## Two bugs the test caught

Two results changed the script. We ran the 5.1.90 test again with the prefix rule ("skip anything starting with 5.1"). It logged 18 rows as rejected: 17 people whose only problem was the quota, plus the one real bad address. A later run would never have mailed those 17.

The second was a half-written log line. We cut the last line of a log in half, as a crash during a write would. The first version then appended its next entry to the broken line.

That entry became unreadable, and a second run sent `person11@example.com` a duplicate. The version above starts a new line first; the same test then sent nobody twice.

This test can't show how Gmail or Exchange Online time their replies, or at which step they refuse. Nor does it show whether they count refused recipients.

One gap remains by design. If the connection drops after the server accepts a message but before the log line is written, a re-run sends that message again. That is why the script tells you to check first.

## When a mailbox is the wrong tool

Both vendors say the ordinary mailbox is not for bulk mail. Microsoft tells customers who send "legitimate bulk commercial email (for example, customer newsletters)" to "use third-party providers that specialize in these services." Google's [sender guidelines](https://support.google.com/a/answer/81126?hl=en) set extra rules for anyone sending more than 5,000 messages a day to Gmail accounts: SPF, DKIM and DMARC, one-click unsubscribe on marketing mail, and a spam rate in Postmaster Tools below 0.3%.

So use this script for transactional or one-to-one mail from your own mailbox: invoices, reminders, renewal notices to existing customers, internal notices. Don't use it for a newsletter, for cold outreach or for anything people did not ask for. It sends one message at a time from one person's account, which suits a few hundred expected messages, not a campaign.

Microsoft 365 adds a deadline. The script signs in with a password, and Microsoft [turns that kind of SMTP sign-in off by default](https://techcommunity.microsoft.com/blog/exchange/updated-exchange-online-smtp-auth-basic-authentication-deprecation-timeline/4489835) at the end of December 2026; we covered the replacements, including Graph `sendMail` and High Volume Email for internal recipients, in [our SMTP AUTH piece](/articles/microsoft-365-smtp-auth-basic-retirement/). Graph has its own ceiling of [10,000 requests per 10 minutes and four concurrent requests](https://learn.microsoft.com/en-us/graph/throttling-limits) per app and mailbox, and Microsoft's [mailbox limits](https://learn.microsoft.com/en-us/office365/servicedescriptions/exchange-online-service-description/exchange-online-limits) apply to "users and applications" alike. For a Google Sheet with a PDF per row, [our mail merge guide](/articles/mail-merge-google-sheets/) does the same pacing inside Apps Script with `MailApp.getRemainingDailyQuota()`.

## Set your numbers before the first run

1. **Find your route** in the first table: personal Gmail, Workspace by Gmail or SMTP, Workspace relay, Apps Script or Microsoft 365. A Workspace trial or a tenant under 60 days old has lower limits.
2. **Take the smallest limit that applies.** For one recipient per message to outside addresses on Workspace, that is 2,000 unique external recipients. On Microsoft 365 it is the tenant's outside-recipient figure in the admin center report, shared with everyone, not the mailbox's 10,000.
3. **Set `--daily-cap` well below it** and leave room for the person's own mail. Our suggestion, not a vendor rule: 400 on personal Gmail, 1,500 on Workspace, and on Microsoft 365 a share of the tenant figure you have agreed with whoever runs it.
4. **Keep `--per-minute` at 20 or less** on Microsoft 365, under its 30.
5. **Send from your own domain,** never an onmicrosoft.com address, which stops at 100 outside recipients a day for the whole tenant.
6. **Run once with `--dry-run`,** then once to a CSV holding only your own address, then the real list.

## How we made this

We read Google's and Microsoft's limit, quota, error-code and sender pages on October 27, 2026, and Microsoft's message center post MC1454397 through a public mirror. Microsoft's own blog post on the tenant limit blocked our requests. We wrote the script and ran the 7 tests in the table and the half-written log test against a local SMTP server, on 25 invented rows; two comparison runs used earlier versions. We did not send through Gmail, Workspace or Microsoft 365, so how those services reply at their limits is taken from their documentation, not observed.

## Sources

- Google, [Limits for sending and getting mail](https://support.google.com/mail/answer/22839?hl=en) (read October 27, 2026)
- Google Workspace, [Gmail sending limits in Google Workspace](https://knowledge.workspace.google.com/admin/gmail/gmail-sending-limits-in-google-workspace?hl=en) (updated October 22, 2026)
- Google Workspace, [Route outgoing SMTP relay messages through Google](https://knowledge.workspace.google.com/admin/gmail/advanced/route-outgoing-smtp-relay-messages-through-google?hl=en) (updated October 22, 2026)
- Google Workspace, [Gmail SMTP errors and codes](https://knowledge.workspace.google.com/admin/support/troubleshooting/gmail-smtp-errors-and-codes?hl=en) (updated October 22, 2026)
- Google Workspace, [Email sender guidelines](https://support.google.com/a/answer/81126?hl=en) (read October 27, 2026)
- Google, [Gmail API usage limits](https://developers.google.com/workspace/gmail/api/reference/quota) (updated October 16, 2026)
- Google, [Quotas for Google services, Apps Script](https://developers.google.com/apps-script/guides/services/quotas) (updated September 3, 2026)
- Microsoft Learn, [Exchange Online limits](https://learn.microsoft.com/en-us/office365/servicedescriptions/exchange-online-service-description/exchange-online-limits) (updated October 19, 2026)
- Microsoft Learn, [Email nondelivery reports and SMTP errors in Exchange Online](https://learn.microsoft.com/en-us/troubleshoot/exchange/email-delivery/ndr/non-delivery-reports-in-exchange-online) (updated August 24, 2026)
- Microsoft Learn, [Microsoft Graph service-specific throttling limits](https://learn.microsoft.com/en-us/graph/throttling-limits) (read October 27, 2026)
- Microsoft 365 message center, [MC1454397: Updates to Tenant External Recipient Rate Limit quotas](https://mc.merill.net/message/MC1454397), via mc.merill.net (published August 14, 2026, updated September 29, 2026)
