Project Story → About the project
Paste everything below this line into the "About the project" box.
Inspiration
I didn't start this for a hackathon. I started it because I was the person with the problem.
If you're a student outside the US, scholarship websites aren't built for you. Fastweb, Bold.org, Scholly — they assume an American undergraduate filling out FAFSA. Going Merry, one of the better ones, shut down entirely in March 2026. So funding reaches students here a completely different way: WhatsApp groups, Telegram channels, and email newsletters that fire off dozens of forwarded posts a week, in a mix of Arabic and English, half of them already expired.
I was in several of those groups. And the actual experience isn't "I can't find scholarships" — it's the opposite. You're drowning in them. You open forty forwarded messages and thirty-eight are closed to your nationality, or for a PhD when you want a master's, or the deadline passed last Tuesday. You do that filtering by hand, in your second language, at midnight, while someone with a paid consultant gets handed a filtered shortlist.
Then I looked at the AI scholarship tools that have appeared recently, and every single one optimises the same number: how many matches it found. That's the wrong number. Nobody's problem is too few results. The scarce thing is your attention, and the real cost is spending it on applications you were never eligible for.
So I built the tool I actually wanted: one that tells me what I can't get, and proves it.
What it does
Scholarship Radar is an autonomous agent rather than a search box. It runs on a schedule, decides its own next move, and only reports things it can justify.
It reads you, not a form. You write a paragraph in whatever language you think in. I tested it heavily in Arabic. It pulls out nationality, the degree you hold versus the one you're applying for, your field, your GPA on whatever scale you used (3.4/4 and 82% both work), language test scores, and what you can afford. Then it asks one or two follow-up questions about anything it genuinely needs — not twenty fields up front. Ask a student to fill in a form and you lose them. Ask them to describe themselves and they will.
It plans its own searches. Based on your profile and what it has already found, it writes its own queries and changes them run to run, so it explores instead of repeating itself. It searches your region and the countries you named — not the US listings every other tool defaults to.
It watches where scholarships actually circulate. It reads the publications that announce international funding — Opportunity Desk, Scholarship Region, Africademics, ProFellow, Opportunities For Africans — at the source, before those same posts get forwarded into the WhatsApp groups. You can also paste a message straight out of a group and it handles that identically.
It extracts the rules and quotes its evidence. Every eligibility rule comes with the sentence from the listing that states it. If a rule isn't stated, that's recorded as unknown rather than guessed.
And then plain code makes the decision — this is the whole design.
If you ask a language model "is this student eligible?", it answers confidently and it answers wrong. Nationality lists and GPA scales are exactly where it slips. So the model never decides anything. It only transcribes:
"nationality": {
"allow": ["Nigeria"],
"deny": [],
"evidence": "This scholarship is open to Nigerian citizens currently resident in Nigeria."
}
Ordinary TypeScript turns that into a verdict. Every constraint resolves to PASS, FAIL or UNKNOWN, and that rolls up into three outcomes: you qualify, worth checking, or not eligible. Near misses get flagged rather than hidden — a 3.5 GPA against a 3.6 minimum shows up as "just short, worth asking", because that's a phone call, not a closed door. And anything it couldn't confirm says so out loud instead of pretending.
Here's the engine on real listings it found, for a Jordanian master's applicant:
NOT ELIGIBLE Rebecca Charles Trust Scholarship For Nigerians
→ Restricted to Nigeria — not Jordan.
NOT ELIGIBLE EU Intra-Africa PERI ACE Scholarships
→ Restricted to Africa — not Jordan.
NOT ELIGIBLE Jesus College Cambridge Research Fellowship
→ For postdoc study, not masters.
WORTH CHECKING La Trobe Graduate Research Scholarship
→ No deadline found — verify on the official page.
YOU QUALIFY University of Illinois Graduate Scholarship
→ All requirements met. "Eligible Countries: All Countries"
Every rejection there is an evening someone gets back.
There's also an Activity page that shows the agent's own reasoning — the queries it chose, what it read, what it recognised as a duplicate it already had, and how it judged each result. I wanted to be able to check its work rather than trust it.
How we built it
Next.js 16 and TypeScript for the app, Neon Postgres with Drizzle for storage, Google Gemini on the free tier for the model calls, all deployed on Vercel. Zod schemas do double duty: they type the database rows and validate what comes back from the model.
Two structural decisions carried everything else.
Raw messages and opportunities are separate tables. A raw message is one immutable post from one source at one moment. An opportunity is the canonical, deduplicated award. The same Chevening announcement arriving six times from four different channels becomes six raw messages, one opportunity, and six provenance records. Without that split the feed just fills with copies of the same thing — which is exactly what makes the WhatsApp groups exhausting in the first place.
Every channel shares one pipeline. Web discovery, pasted forwards, and Telegram all call the same ingestText() function. Adding a new source is a small adapter, not a new pipeline.
For the model layer, every call goes through one LLMProvider interface. Gemini is the implementation today; Groq is wired up as a fallback for when the free quota runs out. Swapping providers is a config change, which mattered more than I expected once I started hitting free-tier limits.
The eligibility engine is the part I spent the most care on, and it's plain TypeScript with 18 unit tests. Not a model call in it.
Challenges we ran into
The documented free path didn't work. My whole plan rested on Gemini's Google Search grounding, which the docs describe as available on the free tier. A brand new key returns 429 RESOURCE_EXHAUSTED on the very first grounded request — and on every model I tried. It needs billing enabled. I found this out at about 2pm on build day with the whole discovery layer depending on it.
Rather than paywall a project meant for students who can't pay, I rebuilt discovery on curated RSS from the outlets that announce international scholarships. It turned out to be a better design than the one I lost: those are the exact posts that later get forwarded into the WhatsApp groups, so the agent now reads them at the source instead of waiting for the forward.
Free-tier models fail in ways a demo won't survive. Gemini Flash returns 503 under load often enough that a single attempt is unusable for anything running unattended. I added retry with backoff plus a fallback chain across the model family, since capacity pressure is rarely simultaneous across all of them.
Serverless punishes a greedy agent. My first scheduled run swept eight items, overran Vercel's 60-second function limit, and returned a 504 having thrown away everything it had done. Agent runs are now sized to fit the window — three feed items and one planned query per tick — and resume where the last one stopped. Small and frequent beats big and killed.
A unit test caught a bug that would have defeated the entire point. My country matcher used substring comparison, so it treated "Nigeria" and "Niger" as the same country. That would have told a Nigerian student they qualified for a Niger-only award — the precise false positive this project exists to prevent. A test caught it, not a user.
Accomplishments that I'm proud of
The eligibility engine is unit-testable, and I don't think any of the commercial tools in this space can say that. Because the model only transcribes rules and code makes the decision, I can write a test that asserts a Jordanian applicant is correctly rejected from a US-only scholarship with the right clause quoted. That's a real guarantee, not a vibe.
It's also free per student, and that isn't an accident. Rules are extracted once per opportunity, so evaluating them against any number of students costs zero API calls. Continuous monitoring for everyone is genuinely affordable on a free tier, which is the only reason this design works for the people who need it.
And it's honest. When it can't confirm a deadline it says so and still shows you the opportunity. When it rejects something it shows you the sentence it rejected you on. I care about that more than any feature, because a scholarship tool that overstates its matches has recreated the exact problem I built it to solve.
What we learned
That the interesting engineering wasn't the AI part — it was deciding what to stop asking the AI to do. My first instinct was to have the model judge eligibility directly. It's the obvious design and it produces confident nonsense. Pulling the decision out of the model and into ordinary code made the system testable, free to run per user, and auditable, all at once.
I also learned how much of building on free tiers is engineering around failure rather than writing features. Roughly a third of my time went into retries, fallbacks, and time budgets — none of which show up in a demo, all of which are the difference between something that works once and something that runs unattended.
And I learned to check what I'm actually deploying. My production URL turned out to be taken by a different Vercel user, and I nearly submitted a link to a stranger's project.
What's next for Scholarship Radar
- Accounts, so a profile isn't tied to one browser
- Gmail newsletter ingestion, read-only and scoped to a label you choose
- Re-verification sweeps: deadline moved, link dead, new cycle opened — nobody currently re-checks
- Enabling the Telegram bot on the live deployment (the adapter is written and in the repo)
- Reusing essays and recommendation letters across applications
- An effort-versus-odds score. A $500 award needing three essays and two reference letters is a bad trade, and no tool currently tells you that
Built With
- ai-agents
- drizzle-orm
- gemini-api
- google-gemini
- llm
- neon
- next.js
- node.js
- postgresql
- react
- rss
- structured-outputs
- tailwindcss
- typescript
- vercel
- vitest
- zod
Log in or sign up for Devpost to join the conversation.