Skip to content

App using Python made for testing Graph API user:SendMail upper limits, while respecting the Microsoft Graph service-specific throttling limits

Notifications You must be signed in to change notification settings

dylanstetts/User-SendMail-Limits

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 

Repository files navigation

Microsoft Graph Mail API Rate Limit Tester

This application tests the rate limits of Microsoft Graph API's sendMail endpoint using app-only authentication with client secret credentials.

Purpose

  • Test Microsoft Graph API rate limits (10,000 requests per 10 minutes = 1,000 requests/minute per mailbox)
  • Support up to 5 sender mailboxes for concurrent distributed testing
  • Send emails with random subjects and bodies to a configurable recipient
  • Track success rates, failures, and rate limit responses
  • Concurrent sending: Each mailbox sends independently with parallel requests for high-volume testing
  • Scalable architecture: Automatically adjusts between sequential (low-rate) and batch concurrent (high-rate) modes

Features

  • App-Only Authentication: Uses MSAL with client credentials flow
  • High-Volume Concurrent Mode: Uses parallel requests (up to 4 concurrent per Graph API limits)
  • Adaptive Architecture: Switches between sequential (low-rate) and batch concurrent (high-rate) modes
  • Graph API Compliant: Respects 4 concurrent request limit per application
  • Connection Pooling: Optimized HTTP connection management for sustained throughput
  • Precise Timing Control: Compensates for API request duration to maintain accurate send rates
  • GUI Interface: Easy-to-use graphical interface with rate presets (30, 100, 500, 1000/min)
  • API Request Logging: Detailed logging of all Graph API requests and responses to graph_api_log.txt
  • Configurable Testing: Adjust emails per minute (per mailbox), test duration, and number of mailboxes
  • Real-time Monitoring: Live progress updates and aggregate statistics
  • Configuration Persistence: Save and load test configurations
  • Comprehensive Logging: Detailed logging of all operations

Prerequisites

1. Azure AD App Registration

You need to register an application in Azure AD with the following setup:

  1. Go to Azure Portal → Azure Active Directory → App registrations
  2. Click "New registration"
  3. Set a name (e.g., "Mail Rate Limit Tester")
  4. Set "Supported account types" to "Accounts in this organizational directory only"
  5. Click "Register"

2. Configure API Permissions

After registration, configure the following:

  1. Go to "API permissions"
  2. Click "Add a permission" → "Microsoft Graph" → "Application permissions"
  3. Add the following permission:
    • Mail.Send (to send mail as any user)
  4. Click "Grant admin consent" (requires admin privileges)

3. Create Client Secret

  1. Go to "Certificates & secrets"
  2. Click "New client secret"
  3. Add a description and set expiration
  4. Copy the secret value immediately (you won't be able to see it again)

4. Get Application IDs

Note the following from the app registration overview:

  • Application (client) ID
  • Directory (tenant) ID

5. Configure Sender Mailboxes

Ensure you have valid user mailboxes in your tenant that will be used as senders. The app will send emails on behalf of these users using the Mail.Send application permission.

Installation

  1. Install Python dependencies:
pip install -r requirements.txt

Configuration

Using the GUI (Recommended)

  1. Run the application:
python main.py
  1. Fill in the configuration fields:

    • Tenant ID: Your Azure AD tenant ID
    • Client ID: Application (client) ID from app registration
    • Client Secret: The secret value you created
    • Recipient Email: Email address that will receive test messages
    • Sender Mailboxes: Enter 1-5 email addresses (one per line) that will send the messages
  2. Configure test parameters:

    • Target Emails/Minute (per mailbox): Set your desired rate (default: 30)
    • Quick Presets: Use preset buttons for common rates:
      • 30/min: Exchange Online typical limit
      • 100/min: Moderate volume testing
      • 500/min: High volume testing
      • 1000/min: Graph API maximum (10k per 10 minutes)
    • Test Duration (minutes): How long to run the test
  3. Click "Save Config" to save your configuration for future use

Configuration File

The app saves configuration to mail_tester_config.json. Example:

{
  "tenant_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "client_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "client_secret": "your-secret-here",
  "recipient_email": "testrecipient@yourdomain.com",
  "sender_mailboxes": [
    "sender1@yourdomain.com",
    "sender2@yourdomain.com"
  ],
  "emails_per_minute": 30,
  "test_duration_minutes": 5
}

Usage

Running a Test

  1. Start the application
  2. Ensure configuration is complete
  3. Click "Start Test" button
  4. Review the confirmation dialog showing aggregate targets
  5. Monitor real-time progress in the log window
  6. View statistics: Sent, Failed, Rate Limited, Actual Rate
  7. Click "View API Log" to see detailed Graph API requests/responses
  8. Click "Stop Test" to abort early if needed

API Request Logging

The application logs all Graph API requests and responses to graph_api_log.txt in the same directory. This includes:

  • Full request URL, headers, and body
  • Response status, headers, and body
  • Request duration in seconds
  • Timestamps for each call

Click the "View API Log" button in the GUI to open this file in your default text editor during or after a test run.

Understanding Results

The application tracks:

  • Sent: Successfully sent emails (HTTP 202)
  • Failed: Errors other than rate limiting
  • Rate Limited: Emails that received HTTP 429 (rate limit)
  • Actual Rate: Measured emails/minute achieved

Expected Behavior

According to Microsoft Graph API limits:

  • Graph API concurrency limit: 4 concurrent requests per application (critical throttling limit)
  • Graph API rate limit: 10,000 requests per 10 minutes per mailbox = 1,000 requests/minute maximum
  • Exchange Online sending limit: Approximately 30 messages/minute per mailbox (separate from Graph API)
  • Using multiple sender mailboxes allows concurrent sending for aggregate throughput
  • With 5 mailboxes at 4 concurrent requests: Can sustain high throughput while respecting API limits
  • Each mailbox operates independently with its own rate limiting
  • Rate limiting (HTTP 429) will occur if:
    • More than 4 requests are made concurrently
    • More than 1,000 requests/minute per mailbox
    • More than 10,000 requests per 10-minute window per mailbox
  • The Retry-After header indicates when to retry

Performance Modes

The application automatically selects the optimal sending mode:

Low-Rate Mode (≤100 emails/min per mailbox):

  • Sequential sending with precise timing
  • One request at a time per mailbox
  • Optimized for accuracy over throughput

High-Volume Mode (>100 emails/min per mailbox):

  • Batch concurrent sending
  • Up to 4 parallel requests per mailbox (Graph API limit)
  • Connection pooling for optimal performance
  • Burst sending with timed delays between batches
  • Can sustain 200-400+ emails/minute per mailbox while respecting API limits

Important Notes

Security

⚠️ IMPORTANT: The client secret is sensitive information:

  • Never commit mail_tester_config.json to version control
  • Store secrets securely (e.g., Azure Key Vault in production)
  • Use least-privilege permissions (only Mail.Send)
  • Rotate secrets regularly

Rate Limits

Microsoft Graph and Exchange Online have multiple rate limits:

  • Per-mailbox limits: 30 messages/minute per sender
  • Per-app limits: May vary based on tenant size
  • Throttling: May occur under heavy load

Mailbox Configuration

  • Ensure sender mailboxes exist and are active
  • The app requires Mail.Send application permission (app-only context)
  • No user interaction is required (daemon/service scenario)

Troubleshooting

Authentication Fails

  • Verify tenant ID, client ID, and client secret are correct
  • Ensure admin consent was granted for Mail.Send permission
  • Check that the app registration is in the correct tenant

Cannot Send Emails

  • Verify sender mailbox addresses are valid
  • Ensure Mail.Send application permission is granted
  • Check that mailboxes are not disabled or restricted
  • Review error messages in the log window

Rate Limiting

  • This is expected behavior when testing limits
  • Note the actual rate achieved before hitting limits
  • Use multiple sender mailboxes to distribute load

Architecture

  • Authentication: MSAL with ConfidentialClientApplication (client credentials flow)
  • Concurrency: asyncio.gather() for parallel task execution across all mailboxes
  • HTTP Client: aiohttp for async HTTP requests
  • Thread-Safe Stats: asyncio.Lock for coordinating statistics across concurrent tasks
  • API: Microsoft Graph v1.0 /users/{id}/sendMail endpoint
  • GUI: tkinter with threading for async operations
  • Logging: Python logging module with GUI and console output

How Concurrent Sending Works

Multi-Level Concurrency Architecture:

  1. Mailbox-level concurrency: Each sender mailbox runs as an independent async task
  2. Request-level concurrency: For high rates (>100/min), each mailbox sends multiple requests in parallel
  3. All mailbox tasks execute concurrently using asyncio.gather()
  4. Connection pooling: Up to 100 connections per host for sustained high throughput
  5. Adaptive batching: Automatically calculates optimal batch size based on target rate
  6. Statistics are updated in a thread-safe manner using async locks

Example with 5 mailboxes at 1000/min each:

Mailbox 1 ──→ [50 concurrent requests × 20 batches/min] ─┐
Mailbox 2 ──→ [50 concurrent requests × 20 batches/min] ─┤
Mailbox 3 ──→ [50 concurrent requests × 20 batches/min] ─┼──→ ~5,000 emails/min
Mailbox 4 ──→ [50 concurrent requests × 20 batches/min] ─┤
Mailbox 5 ──→ [50 concurrent requests × 20 batches/min] ─┘

Timing Accuracy Improvements

The application uses several techniques to achieve accurate send rates:

Low-Rate Mode (<100/min):

  1. High-precision timing: Uses time.perf_counter() for sub-millisecond accuracy
  2. Duration compensation: Measures actual API request time and schedules next send accordingly
  3. Pre-scheduling: Calculates exact send time before each request
  4. No cumulative drift: Each send is scheduled independently

High-Rate Mode (>100/min):

  1. Batch timing: Sends bursts of parallel requests with controlled delays
  2. Semaphore control: Limits concurrent requests to prevent overwhelming the API
  3. Adaptive pacing: Adjusts batch intervals based on actual batch completion time
  4. Connection reuse: HTTP connection pooling eliminates connection overhead

References

License

This is a testing tool. Use responsibly and in accordance with your organization's policies.

About

App using Python made for testing Graph API user:SendMail upper limits, while respecting the Microsoft Graph service-specific throttling limits

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages