In high school (2016), a number of students started taking dual enrollment classes at a local city college. Dual enrollment students had the lowest registration priority, so our classes were always full and waitlisted. To help us get in, I made a script that would check the waitlists for availability and send an email when there was an opening. This proved to be very effective, and I ended up building a simple website around it. By senior year (2019) almost every student that was taking dual enrollment classes was using the website. During the summer before college, I rebuilt the project into Class Alerts, ultimately supporting eight colleges.
Unfortunately, over the following years, only a few dozen people used the website. This was less than necessary to pay for expenses, so in December 2022, I shut it down.
In this short post, I want to highlight a few features and lessons learned.
- Supports 8 colleges
- Checks each class status every 5 seconds
- Customizable notifications via Email (Mailgun), SMS Text, or Phone Call (Twilio)
- Email verification and password resets
- Payments using PayPal
- Built on Flask, with SQLAlchemy
- Authentication via Email/Password or Google OAuth
- Rate-limited API
- Uses TOR to bypass IP-rate-limited colleges
I was first exposed to Python decorators in this project. I ended up building a powerful system for describing API endpoints. Here's an excerpt:
@app.route("/api/contact", methods=["POST"]) @requires_form_field("email", if_missing="Email missing", redirect_url_for="contact_page", value_pattern=PATTERN_EMAIL) @requires_form_field("subject", if_missing="Subject missing", redirect_url_for="contact_page", value_pattern=PATTERN_NOT_EMPTY) @requires_form_field("message", if_missing="Message missing", redirect_url_for="contact_page", value_pattern=PATTERN_NOT_EMPTY) @limiter.limit("5 per day") def api_send_contact(): ...
It included decorators for requiring certain permissions (signed-in, admin, paid, etc.). It automatically repopulated fields on errors and handled CSRF protection.
I never intended for this project to become anything massive or profitable. But I did hope it would pay for itself, and I could count on some organic growth to sustain it. Unsurprisingly, that's a hard place to get.
Growth is hard: I had a very established competitor (Coursicle). Although their service was worse in some ways (checking every 5 minutes, only in-app notifications), they offered 100s of colleges, and could afford to offer a version of their service for free. They were VC-backed, and spent on advertising. I focused on an improved experience for a limited number of colleges, even spending around $50 on targeted Google Ads, to no avail. My dedicated user-base from high school dwindled, and few users came in to replace them.
LLCs are not free: I spent around $200 to create a California-based LLC, followed by $800 in minimum taxes despite not making any money. I was a bit scared of liability, but there was little reason for this at my scale, and I terminated it after a year. I can say that I was a CEO—but is that worth $1000? Don't think so.
Helping people is rewarding: So why did I put so much time and money into this project? It's a bit cliché, but being able to help my friends and classmates was the most rewarding aspect. And this didn't really scale past a small group: class enrollment is a zero-sum game. The more people that use the service, the less they benefit.
The project was an educational venture. It was not the right project to turn into a business. But it prepared me in case I ever stumble upon one that is. And of course, I learned a bunch of new tech and was able to help a handful of people.
The full code is now public on GitHub. You can visit some parts of the site on the Internet Archive.