Inspiration You know that feeling when you find out about an amazing hackathon or fellowship after the deadline passed, that happened to me alot and it wasn't the first time. Singapore has a genuinely insane number of opportunities for youths (hackathons, fellowships, gov schemes, arts grants, mental health programs) but they're scattered across fifteen different Telegram channels, school emails, and org websites that nobody checks. There's no "one place" (at least that i know of) so most students just don't find out until it's too late, or not at all. I built YouthScout SG to be that one place. What it does YouthScout SG scrapes and aggregates every youth opportunity in Singapore — hackathons, fellowships, internships, bootcamps, arts grants, gov schemes, mental health programs, the works — into a single swipeable feed. Think Tinder, but instead of matching with people you're matching with your next internship.

Swipe right to save, swipe up to mark applied, swipe left to skip Personalised onboarding, tell us your interests and what you're looking for, and the feed re-ranks by relevance Calendar export (.ics) for every deadline you save, so it lands straight in your Google/Apple/Outlook calendar with a 7-day reminder Intern Reviews , a crowdsourced + scraped Glassdoor-for-interns, because no one tells you DBS hours are rough or that GovTech's mentorship is actually great until you're already three weeks in, and its hard to find reviews these days online Installable as a PWA with push notifications when new opps drop Auth + saved/applied state synced via Supabase so your list follows you across devices

74+ opportunities live right now across categories, auto-refreshed. How we built it Solo build, full stack:

Scraping: Python + BeautifulSoup hitting Devpost, MLH, Eventbrite, SGInnovate, CSIT, WaveSparks, Meetup, plus Google Custom Search API as a catch-all for sources without clean structure. Runs on a schedule via GitHub Actions, writes to JSON, deployed on render, so there's zero server cost. Frontend: vanilla JS, no framework, kept it lightweight on purpose since this needs to load fast on a phone in 4G on the MRT. Card stack uses real drag-physics (mousedown/touchstart → transform translate+rotate → threshold-based fly-out animation), not a library. Auth & data: Supabase for auth, profiles, and the intern reviews table. Hit a nasty bug where the auth state listener was double-firing the boot logic on token refresh, turned out I had a typo in my SIGNED_OUT event string check that was silently failing the early-return guard, so every token refresh re-ran onboarding logic. Took a while to catch because it only showed up after the access token's natural expiry window, not immediately on login. PWA: full service worker with cache-busting on deploy, beforeinstallprompt handling, and Notification API integration for new-opportunity alerts. Design: leaned hard into a "boarding pass / passport" visual metaphor, perforated card edges, stamp-style status badges, a "passport strip" instead of a normal navbar, because youth opportunities are basically a passport to your next chapter, and Gen Z UI shouldn't look like LinkedIn.

Challenges we ran into

The Supabase auth race condition mentioned above ate a full evening. Classic "it works on my machine" until the token actually expires. Junk filtering: scraped data is messy, cookie banners, "click here" links, and nav fragments kept sneaking into the opportunities feed as if they were real listings. Had to write a heuristic junk filter (title length bounds, denylist, URL-pattern detection) to keep the feed clean without manually curating every entry. Swipe-deck height collapsing on certain mobile viewports because flexbox computed 100% of a 0-height parent on first paint, needed a JS-computed fallback height rather than relying on pure CSS. Merging two separate data domains (opportunities + youth programs) into one swipeable deck without breaking the existing card renderer , solved by normalising both into the same shape so buildCard() never had to know the difference.

Accomplishments that we're proud of

A real, working, end-to-end product, not a static prototype , with live auth, real scraped data, and people who could actually use this tomorrow. Solo-built the full stack: scraping pipeline, backend, frontend, PWA, design system, all of it. The swipe mechanic isn't just a gimmick, it makes browsing 70+ opportunities feel like 5 minutes instead of an hour of scrolling a spreadsheet. Built Intern Reviews as a second product surface on top of the same infra, because saving an opportunity is only half the problem, knowing if it's actually worth your time is the other half.

What's next for YouthScout SG

Telegram channel scraping via Telethon, a huge chunk of youth opportunities get posted only in Telegram groups, and right now we're blind to that entire channel Smarter relevance scoring (move past keyword matching toward embedding-based similarity between user interests and opportunity descriptions), maybe even include ai and new features making it personalized Push notifications that are actually personalised, not just "new opp dropped" Opening up intern reviews submission more widely and adding moderation tooling so it scales past manual review Eventually: a public API so school CCA pages / poly career centres could embed a live YouthScout feed instead of a static PDF list that's outdated by week two

Built With

Share this project:

Updates