Find and track LeetCode users from your college campus!
Ever wondered which of your college friends is secretly solving LeetCode at 2 AM instead of doing assignments? This extension lets you totally not creepily find every LeetCoder in your campus. You can spot hidden tryhards, sleepy grinders, fake humble toppers, and all the people who pretend they barely code but actually never stop, lol. Below is a detailed documentation on my approach towards this solution.
To avoid rate limits, I've set the scraping limit to 2 pages per day and 35 usernames, so for the extension to work fully properly, it will take 6 months to scrape majority of the usernames from the leetcode website unless the GraphQL query scheme is changed by leetcode themselves.
- Features
- Project Structure
- Prerequisites
- Installation & Setup
- Hosting the Backend on Render
- Alternative: Using Supabase for Database (Not Implemented)
- Usage
- API Endpoints
- How It Works
- Troubleshooting
- Contributing
- License
- College-based Search: Find LeetCode users from your specific college or university
- User Statistics: View comprehensive stats for each user including:
- Real name and school affiliation
- Global rankings
- Country information
- Direct links to LeetCode profiles
- Real-time Updates: Automatically scrape and update user data via background tasks
- Campus Discovery: Compare and discover coding activity within your campus community
- Chrome Extension: Easy-to-use browser extension interface with search and filter capabilities
- Local Caching: Fast searches using locally cached user profiles stored in JSON format
- RESTful API: FastAPI-powered backend with comprehensive endpoints for data access
LeetSearch/
├── backend/ # Backend server for data scraping and API
│ ├── main.py # FastAPI application and REST endpoints
│ ├── models.py # Pydantic data models for API contracts
│ ├── scraper.py # LeetCode GraphQL scraper with authentication
│ ├── updater.py # Background task for periodic data updates
│ ├── search.py # Search utilities and helpers
│ ├── requirements.txt # Python dependencies
│ ├── users.json # Local JSON database cache (auto-generated)
│ └── test_main.py # Unit tests for backend endpoints
├── extension/ # Chrome extension files
│ ├── manifest.json # Extension manifest configuration
│ ├── popup.html # Extension popup UI structure
│ ├── popup.js # Extension logic and API communication
│ └── style.css # Extension styling
├── .gitignore # Git ignore patterns
├── LICENSE # Apache 2.0 License
└── README.md # This comprehensive guide
Before you begin, ensure you have the following:
- Python 3.7+ installed on your system
- pip (Python package manager)
- Google Chrome or any Chromium-based browser (Edge, Brave, etc.)
- LeetCode Account (required for accessing LeetCode's GraphQL API)
- Text Editor (VS Code, Sublime Text, etc.) for configuration files
- Git (optional, for cloning the repository)
The backend needs to authenticate with LeetCode's GraphQL API to fetch user profiles and contest data. LeetCode requires session cookies for accessing their internal APIs.
LeetCode's public profile data and contest rankings are behind authentication. To programmatically access this data:
- LEETCODE_SESSION: This is your session cookie that identifies you as an authenticated user to LeetCode's servers
- CSRFTOKEN: Cross-Site Request Forgery token used for security verification on POST requests to LeetCode's API
Without these tokens, the scraper cannot access LeetCode's GraphQL endpoints and will fail to fetch user data.
-
Open Chrome/Firefox and navigate to https://leetcode.com
-
Log in to your LeetCode account
-
Open Developer Tools:
- Chrome/Edge: Press
F12orCtrl+Shift+I(Windows/Linux) orCmd+Option+I(Mac) - Firefox: Press
F12orCtrl+Shift+I(Windows/Linux) orCmd+Option+I(Mac)
- Chrome/Edge: Press
-
Navigate to the Application/Storage Tab:
- In Chrome/Edge: Click on the "Application" tab
- In Firefox: Click on the "Storage" tab
-
Find Cookies:
- Expand "Cookies" in the left sidebar
- Click on
https://leetcode.com
-
Locate and Copy Tokens:
- Find the cookie named
LEETCODE_SESSIONand copy its Value - Find the cookie named
csrftokenand copy its Value
The values will be long alphanumeric strings. Copy these values carefully.
- Find the cookie named
-
Keep These Tokens Secure: These tokens give access to your LeetCode account, so never share them publicly or commit them to version control.
git clone https://github.com/saaj376/LeetSearch.git
cd LeetSearchcd backendpip install -r requirements.txtThe requirements.txt includes:
fastapi- Modern web framework for building APIsuvicorn- ASGI server for running FastAPIrequests- HTTP library for making API calls to LeetCodepython-dotenv- Environment variable managementpytest- Testing frameworkhttpx- Async HTTP client for testing
Create a .env file in the backend directory:
touch .envOpen the .env file in your text editor and add your tokens:
LEETCODE_SESSION=your_leetcode_session_token_here
CSRFTOKEN=your_csrf_token_hereImportant Notes:
- Replace
your_leetcode_session_token_hereandyour_csrf_token_herewith the actual tokens you copied earlier - The
.envfile is already included in.gitignoreto prevent accidentally committing your credentials - Never share these tokens or commit them to version control
For local development:
python main.pyThe server will start on http://localhost:8000 with auto-reload enabled.
Alternatively, use uvicorn directly:
uvicorn main:app --host 0.0.0.0 --port 8000 --reloadOpen your browser and navigate to:
- API Documentation:
http://localhost:8000/docs - Health Check:
http://localhost:8000/health
You should see the FastAPI Swagger documentation and a JSON response showing the health status.
To populate the database with user data, trigger a refresh cycle:
curl -X POST "http://localhost:8000/refresh" \
-H "Content-Type: application/json" \
-d '{"pages": 2, "max_users": 25}'This will:
- Scrape the first 2 pages of LeetCode's global contest rankings
- Fetch detailed profiles for up to 25 users
- Store the data in
users.json
The process may take several minutes depending on the number of users.
Before loading the extension, you need to configure it to communicate with your backend.
For Local Development:
Open extension/manifest.json and ensure the host_permissions includes your local backend:
{
"host_permissions": [
"http://localhost:8000/*"
]
}Open extension/popup.js and verify the DEFAULT_API_BASE constant:
const DEFAULT_API_BASE = "http://localhost:8000";For Production (Hosted Backend):
If you're hosting the backend on a service like Render (see next section), update both files:
In manifest.json:
{
"host_permissions": [
"https://your-app-name.onrender.com/*"
]
}In popup.js:
const DEFAULT_API_BASE = "https://your-app-name.onrender.com";Replace your-app-name.onrender.com with your actual hosted backend URL.
-
Open Chrome and navigate to
chrome://extensions/ -
Enable Developer Mode:
- Look for the toggle in the top-right corner
- Click to enable it
-
Load the Extension:
- Click the "Load unpacked" button
- Navigate to the
LeetSearch/extensionfolder - Select the folder and click "Open"
-
Verify Installation:
- You should see "LeetCode College Finder" appear in your extensions list
- The extension icon should appear in your Chrome toolbar (you may need to click the puzzle piece icon to pin it)
- Click the extension icon in your Chrome toolbar
- Enter a college name (e.g., "MIT", "Stanford", "Waterloo")
- Click "Search"
- You should see a list of LeetCode users from that college
If you get an error, verify:
- Backend is running and accessible
manifest.jsonhas the correct permissionspopup.jshas the correct API base URL
To make your LeetSearch extension accessible from anywhere, you can host the backend on Render (a free cloud platform).
Ensure your code is pushed to GitHub:
git add .
git commit -m "Prepare for Render deployment"
git push origin main- Go to https://render.com
- Sign up for a free account (you can use your GitHub account)
- From your Render dashboard, click "New +" and select "Web Service"
- Connect your GitHub repository (authorize Render to access your repos)
- Select the
LeetSearchrepository
Fill in the following settings:
- Name:
leetsearch-backend(or any name you prefer) - Region: Choose the closest region to your users
- Branch:
main(or your default branch) - Root Directory:
backend - Runtime:
Python 3 - Build Command:
pip install -r requirements.txt - Start Command:
uvicorn main:app --host 0.0.0.0 --port $PORT
In the "Environment" section, add your tokens:
- Click "Add Environment Variable"
- Add
LEETCODE_SESSIONwith your session token value - Add
CSRFTOKENwith your CSRF token value
- Click "Create Web Service"
- Render will automatically build and deploy your application
- Wait for the deployment to complete (this may take a few minutes)
- Once deployed, you'll receive a URL like
https://leetsearch-backend.onrender.com
Visit your deployed backend's health endpoint:
https://leetsearch-backend.onrender.com/health
You should see a JSON response with the health status.
Visit the API documentation:
https://leetsearch-backend.onrender.com/docs
Follow the instructions in Step 3: Chrome Extension Setup to update the manifest.json and popup.js files with your Render URL.
- Go to
chrome://extensions/ - Find LeetCode College Finder
- Click the refresh icon
- Test the extension with your hosted backend
- Free Tier Limitations: Render's free tier puts your app to sleep after 15 minutes of inactivity. The first request after sleep may take 30-60 seconds to respond.
- Persistent Storage: The
users.jsonfile will be stored on Render's ephemeral disk, which means data may be lost on redeploys. For production use, consider implementing a persistent database solution. - Environment Variables: Never hardcode your tokens in the code. Always use environment variables.
- CORS: The backend already includes CORS middleware to allow requests from browser extensions.
Initially, I considered using Supabase (an open-source Firebase alternative) to host the user profile database online. This would provide:
- Persistent Storage: Data wouldn't be lost between deployments
- Real-time Updates: Multiple users could see synchronized data
- Scalability: Better handling of concurrent requests
- Backup & Recovery: Automatic database backups
- PostgreSQL: Full-featured relational database with complex queries
While integrating Supabase, I encountered several challenges that made the implementation difficult:
-
Authentication Complexity: Supabase's Row Level Security (RLS) policies required additional authentication layer that complicated the simple scraper architecture
-
Schema Migration Issues: Converting the flat JSON structure to Supabase's PostgreSQL schema resulted in:
- Type conversion errors for optional fields
- Null handling inconsistencies
- Complex nested object storage (like
websitesarray)
-
API Rate Limiting: Supabase's free tier has connection limits that conflicted with the scraper's bulk update operations
-
Development Overhead: The additional complexity wasn't justified for a project that works well with local JSON caching
-
Cost Considerations: While Supabase has a free tier, scaling would require paid plans
The current implementation uses a local users.json file for data storage, which:
- âś… Simple and fast for read operations
- âś… No external dependencies or API keys
- âś… Easy to backup and version control
- âś… Works perfectly for college-sized datasets (hundreds to thousands of users)
- ❌ Not suitable for multiple backend instances
- ❌ Data lost on container restarts (on platforms like Render)
If you want to tackle the Supabase integration yourself, here's what you'd need:
-
Create Supabase Project: Sign up at https://supabase.com
-
Create Users Table:
CREATE TABLE profiles ( username TEXT PRIMARY KEY, real_name TEXT, about_me TEXT, country_name TEXT, ranking INTEGER, school TEXT, company TEXT, websites TEXT[], updated_at TIMESTAMP DEFAULT NOW() );
-
Install Supabase Client:
pip install supabase
-
Replace JSON Operations: Update
main.pyto use Supabase client instead of file I/O -
Environment Variables: Add
SUPABASE_URLandSUPABASE_KEYto your.env
-
Launch the Extension:
- Click on the "LeetCode College Finder" icon in your Chrome toolbar
- The popup window will appear
-
Search for a College:
- Enter your college or university name in the search box
- Examples: "MIT", "Stanford", "IIT", "Waterloo", "Berkeley"
- The search is case-insensitive and matches partial strings
- Click the "Search" button or press Enter
-
View Results:
- Users from the matching college will be displayed in a card format
- Each card shows:
- Username (clickable link to their LeetCode profile)
- Global ranking
- Real name (if available)
- Country
- School affiliation
-
Filter Results:
- After a search, a filter box appears
- Type to filter results by username, real name, or country
- The filter updates in real-time as you type
-
Check Statistics:
- The statistics card shows:
- Total profiles matched from your search
- Number of profiles currently displayed (after filtering)
- Last refresh timestamp
- The statistics card shows:
Check if the backend is running and how many profiles are cached:
curl http://localhost:8000/healthResponse:
{
"status": "ok",
"profiles_cached": 150,
"last_updated": "2025-11-24T10:30:00Z"
}Find all users from a specific college:
curl "http://localhost:8000/search/college?query=Stanford"Response:
{
"query": "Stanford",
"total": 5,
"results": [
{
"username": "john_doe",
"realName": "John Doe",
"school": "Stanford University",
"ranking": 1234,
"country": "United States"
}
]
}Retrieve detailed profile for a specific user:
curl http://localhost:8000/profiles/john_doeTo force a fresh fetch from LeetCode:
curl "http://localhost:8000/profiles/john_doe?refresh=true"Manually trigger a background scrape and update cycle:
curl -X POST "http://localhost:8000/refresh" \
-H "Content-Type: application/json" \
-d '{
"pages": 3,
"max_users": 50
}'Parameters:
pages: Number of contest ranking pages to scrape (1-10)max_users: Maximum number of user profiles to fetch (1-200)
Description: Health check endpoint that returns backend status and cache information.
Response:
{
"status": "ok",
"profiles_cached": 150,
"last_updated": "2025-11-24T10:30:00Z"
}Description: Search for users by college/university name.
Query Parameters:
query(required): College name or substring to search for (minimum 2 characters)
Response:
{
"query": "MIT",
"total": 10,
"results": [
{
"username": "alice",
"realName": "Alice Johnson",
"school": "Massachusetts Institute of Technology",
"ranking": 5678,
"country": "United States"
}
]
}Description: Get detailed profile for a specific user.
Path Parameters:
username: LeetCode username
Query Parameters:
refresh(optional): Set totrueto fetch fresh data from LeetCode instead of using cache
Response:
{
"cached": true,
"profile": {
"username": "alice",
"realName": "Alice Johnson",
"aboutMe": "Software Engineer",
"countryName": "United States",
"ranking": 5678,
"school": "Massachusetts Institute of Technology",
"company": "Google",
"websites": ["https://alice.dev"]
}
}Description: Trigger a background task to scrape LeetCode and update cached profiles.
Request Body:
{
"pages": 2,
"max_users": 25
}Response:
{
"started": true,
"pages": 2,
"max_users": 25,
"message": "Refresh cycle scheduled."
}-
FastAPI Server (
main.py):- Exposes RESTful API endpoints
- Handles CORS for Chrome extension requests
- Manages background tasks for data updates
- Implements caching with JSON file storage
-
Scraper (
scraper.py):- Authenticates with LeetCode using session tokens
- Uses GraphQL queries to fetch:
- Global contest rankings (paginated)
- Individual user profiles
- Implements polite rate limiting with random delays
- Handles retries for failed requests
- Rotates user agents to avoid detection
-
Data Storage (
users.json):- Simple JSON file acting as a local database
- Stores cached user profiles with all fetched fields
- Atomic writes to prevent corruption
- Automatically created on first data population
-
Models (
models.py):- Pydantic models for type safety and validation
- Defines data contracts for API requests/responses
- Ensures consistent data structure across the application
-
Popup UI (
popup.html+style.css):- Clean, minimal interface
- Input field for college search
- Results display with user cards
- Statistics dashboard
- Filter functionality
-
Business Logic (
popup.js):- Communicates with backend API
- Handles user interactions (search, filter)
- Manages Chrome storage for API URL persistence
- Renders dynamic content based on API responses
-
Manifest (
manifest.json):- Defines extension metadata and permissions
- Specifies host permissions for backend communication
- Configures popup behavior
-
Initial Setup:
User -> Backend Setup -> .env tokens -> Scraper Authentication -
Data Population:
Refresh Trigger -> Scrape Contest Rankings -> Fetch User Profiles -> Cache to JSON -
College Search:
Extension UI -> Search Input -> Backend API -> Filter JSON Cache -> Return Results -> Display Cards -
Profile Refresh:
Extension/API Request -> Check Cache -> Optionally Fetch Fresh Data -> Update Cache -> Return Profile
Problem: Backend can't authenticate with LeetCode.
Solution:
- Verify
.envfile exists inbackenddirectory - Check tokens are correctly copied (no extra spaces)
- Ensure tokens haven't expired (they can expire after a few weeks)
- Re-obtain fresh tokens from LeetCode following Step 1
Problem: LeetCode is rate limiting or blocking requests.
Solution:
- Wait a few minutes before trying again
- Check if your tokens are still valid
- Reduce
pagesandmax_usersin refresh requests - The scraper already includes delays and retries, but aggressive scraping can still trigger limits
Problem: JSON file was corrupted during write.
Solution:
- Delete
backend/users.json - Restart backend server
- Trigger a new refresh cycle
Problem: Port 8000 is already being used.
Solution:
# Find and gracefully terminate the process using port 8000
lsof -ti:8000 | xargs kill
# If the process doesn't stop, force kill it
lsof -ti:8000 | xargs kill -9
# Or run on a different port
uvicorn main:app --host 0.0.0.0 --port 8001Problem: Extension can't reach backend.
Solution:
- Verify backend is running:
curl http://localhost:8000/health - Check
manifest.jsonhas correcthost_permissions - Check
popup.jshas correctDEFAULT_API_BASE - Look for errors in Chrome DevTools console (right-click extension popup > Inspect)
Problem: Chrome rejects the extension.
Solution:
- Ensure
manifest.jsonis valid JSON (no syntax errors) - Check all referenced files exist (
popup.html,popup.js,style.css) - Look for errors in
chrome://extensions/page
Problem: Database might be empty or college name doesn't match.
Solution:
- Check if database is populated:
curl http://localhost:8000/health - If
profiles_cachedis 0, trigger a refresh - Try different search terms (abbreviations, full names, partial matches)
- Remember: only users who have filled in their school field will be found
Problem: Free tier apps sleep after 15 minutes of inactivity.
Solution:
- Accept the 30-60 second cold start on first request
- Consider upgrading to a paid Render plan for 24/7 uptime
- Use a service like UptimeRobot to ping your app every 14 minutes (keeps it awake)
Problem: users.json is stored on ephemeral disk.
Solution:
- Trigger a refresh after each deployment
- Consider implementing Supabase for persistent storage
- Or use Render's persistent disk feature (paid plans)
Problem: Tokens not accessible in deployed app.
Solution:
- Double-check environment variables in Render dashboard
- Ensure there are no leading/trailing spaces
- Redeploy after adding/updating environment variables
Contributions are welcome! Here's how you can help improve LeetSearch:
- Fork the repository
- Clone your fork:
git clone https://github.com/YOUR_USERNAME/LeetSearch.git cd LeetSearch - Create a feature branch:
git checkout -b feature/AmazingFeature
-
Backend Changes:
- Follow PEP 8 style guide
- Add type hints for new functions
- Update tests in
test_main.py - Run tests:
pytest backend/test_main.py
-
Extension Changes:
- Follow existing code style
- Test thoroughly in Chrome
- Ensure backward compatibility with backend API
-
Documentation:
- Update README.md if adding new features
- Add inline comments for complex logic
- Update API documentation in docstrings
cd backend
pytest test_main.py -v-
Commit your changes:
git add . git commit -m "Add some AmazingFeature"
-
Push to your fork:
git push origin feature/AmazingFeature
-
Open a Pull Request from your fork to the main repository
-
Describe your changes clearly:
- What problem does it solve?
- How did you test it?
- Any breaking changes?
- Python: Follow PEP 8, use type hints, write docstrings
- JavaScript: Use ES6+ features, meaningful variable names
- Commits: Use clear, descriptive commit messages
- Testing: Add tests for new features and bug fixes
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.
Disclaimer: This tool is for educational purposes. Please use LeetCode's API responsibly and respect their rate limits. The tokens you use are tied to your personal LeetCode account - keep them secure and never share them publicly.