A lightweight Flask web UI to invite many users to a GitHub Organization (optionally adding them to a team) by uploading a CSV of email addresses.
- Upload CSV of emails (with or without an
emailheader) - Deduplicates and validates email addresses
- Specify organization and optional team slug
- Processes invitations asynchronously and shows live progress (polling)
- Minimal dependencies
- Python 3.13+
- A GitHub Personal Access Token with proper permissions (see setup below)
- Organization owner/admin permissions on the target GitHub organization
- Log in to GitHub and navigate to Settings → Developer settings → Personal access tokens
- Choose "Tokens (classic)" for broader compatibility
- Click "Generate new token" → "Generate new token (classic)"
- Configure the token:
- Note: Give it a descriptive name (e.g., "Bulk Org Inviter")
- Expiration: Set appropriate expiration (30-90 days recommended)
- Scopes: Select these permissions:
- ✅
admin:org(Admin access to organizations and teams) - ✅
read:org(Read org and team membership)
- ✅
- Click "Generate token" and copy the token immediately (starts with
ghp_)
- Organization Ownership Required: Only organization owners can invite users via API
- Keep Token Secret: Never commit tokens to code repositories
- Token Expiration: Set calendar reminders to renew before expiration
Store the token in the GITHUB_TOKEN environment variable:
Create a .env file:
GITHUB_TOKEN=ghp_your_token_here
(Add .env to your .gitignore file)
- Organization Invitations: ~50 invitations per hour per organization
- API Rate Limit: 5,000 requests per hour for authenticated users
- Secondary Limits: GitHub may impose additional throttling on invitation endpoints
The app automatically handles rate limits by:
- Starting with 0.3-second delays between invitations
- Increasing delays up to 30 seconds when hitting consecutive failures
- Stopping after 10 consecutive failures (likely quota exceeded)
- Providing clear error messages about quota limits
- Small Batches: Process 40-50 emails at a time, then wait an hour
- Off-Peak Hours: Run during low GitHub activity periods
- Monitor Progress: Watch for quota warnings in the UI
- Enterprise: Consider GitHub Enterprise for higher limits
python -m venv myenv
.\myenv\Scripts\Activate.ps1
pip install -r requirements.txt
python app.pyThen open http://localhost:5000/ in your browser.
Any of these will work:
email
alice@example.com
bob@example.com
Or just a single column without header:
alice@example.com
bob@example.com
Or a multi-column file (the column literally named email will be used):
name,email,role
Alice,alice@example.com,dev
Bob,bob@example.com,qa
- GitHub may throttle invite creation; basic pacing (0.3s delay) included.
- Re-inviting an already invited/member email returns as "invited" with note.
- Only last 50 results displayed in UI for brevity; poll JSON has truncated list.
- This simple in-memory job store resets on restart. For production, persist to a DB or cache.
- For very large lists you might raise the delay or implement exponential backoff on 403 rate limits.
- Download full results as CSV
- Cancel job
- Auth protecting the UI
- Persistent storage (Redis / DB)
- Better rate limit/backoff strategy
MIT