Migrating users between Auth0 tenants is a task that sounds straightforward until you start: rate limits bite, payload sizes vary wildly across user profiles, and the window for a zero-downtime migration is narrow. This is a practical guide to the patterns and edge cases we encountered, and the Python implementation that handled them reliably.
Why tenant migration is harder than it looks
Auth0's Management API is powerful but opinionated. The export/import flow works cleanly for small tenants. At scale, thousands of users, custom metadata, linked identities, and MFA enrollments, the assumptions built into the happy path start to fail. Large payloads hit request size limits; incomplete metadata causes silent import failures; rate limiting mid-migration leaves your data in a partially migrated state.
The key challenges
- Payload size limits: user export batches exceeding Auth0's import size threshold need chunking with careful batch sizing
- Rate limiting: the Management API applies rate limits per tenant, naive iteration fails at scale without exponential backoff
- Metadata completeness: custom app_metadata and user_metadata fields must be validated and sanitised before import
- Linked identities: users with linked social or enterprise identities require special handling, the primary identity must be imported first
- MFA enrollments: authenticator enrollments cannot be migrated directly; users must be prompted to re-enrol post-migration
The Python implementation
Our migration script used the Auth0 Python SDK with a chunked export strategy, processing users in configurable batches to stay within payload limits. Each batch included a retry loop with exponential backoff for rate-limited requests, and a structured log that recorded the import status of every user ID, making it safe to resume from any point of failure without re-importing already-migrated users.
Never run a user migration without a dry-run mode and a per-user status log. A migration that can't be safely resumed is a migration that will corrupt your data.
Validation and rollback
Before the live migration, we ran a full dry-run against a test tenant to validate payload compatibility, catch metadata issues, and measure actual throughput against the rate limits. Post-migration, we ran a reconciliation script comparing source and destination user counts, metadata checksums, and login capability, confirming zero data loss before the DNS cutover.
Key learnings
- Chunk size of 500–1,000 users per batch is a reliable starting point for most tenants
- Log every user migration individually, batch-level logging makes diagnosis impossible
- Test MFA re-enrollment UX with real users before go-live: the surprise is worse than the inconvenience
- Keep the source tenant read-only for 48 hours post-migration as a rollback safety net
