Inspiration

What it does

Inspiration

Germany charges international students no tuition. Thousands of Master's programs are taught entirely in English, and a student from Karachi or Lagos can get a world-class engineering degree for the price of a semester contribution.

So why does almost everyone I know pay an agency €1,500 to get there?

Because free tuition isn't the barrier information is. The application runs through uni-assist, which rejects your documents for reasons nobody explains. You need an APS certificate, and the process differs by country. You need a blocked account with an amount that changes every year, an anabin check on your degree, a Studienkolleg year or not depending on your school system, and a visa appointment booked months before the embassy will look at you.

Consultants charge thousands of euros for what is, honestly, research and form-filling. That is exactly the shape of problem AI is good at. CampusConsult exists to do that job for nothing.

What it does

Four stages, from first search to arrival.

Match You build one profile: degree, grades, language level, what you want to study. The advisor searches 2,513 German programs across 236 universities and 189 cities, and ranks them by fit. Every result shows a match score and its reasoning degree, field, language and GPA checked individually so you can see that you're eligible, not just that something was recommended.

Apply A statement of purpose written for the specific program from your profile, not a template with your name pasted in. A German-format CV. Scholarships from DAAD, Erasmus+, foundations and states filtered against your nationality, field and level.

Track Every application on one board: documents, uni-assist status, portal logins, deadlines. The stage where applications usually die quietly in a forgotten spreadsheet.

Succeed Visa checklists by country, blocked-account maths, health insurance, Anmeldung, and real cost of living city by city, because Munich is not Leipzig.

All of it free. No card, no trial, no premium tier holding the useful parts hostage.

How we built it

React 18 + TypeScript + Vite + Tailwind on the front, FastAPI + SQLAlchemy on the back, PostgreSQL for relational data and ChromaDB as a vector index over the program catalogue so search matches on meaning rather than keywords. Azure OpenAI handles both the embeddings and the reasoning and writing.

Everything ships as a single container nginx serving the built frontend, gunicorn with uvicorn workers behind it deployed to Azure App Service for Containers with a managed PostgreSQL Flexible Server. The image builds in Azure Container Registry rather than locally, and the whole deployment is one idempotent shell script.

The landing page uses a scroll-scrubbed video: a 3D animation of wooden letter tiles assembling into MATCH, TRACK, APPLY, SUCCESS, where scroll position drives video.currentTime. Each stage owns a slice of the timeline and is driven by scroll through its own content block, so the word on screen is always the stage you're reading.

Challenges we ran into

The scroll video stuttered. The source had 5 keyframes across 240 frames, so every seek meant decoding forward from a distant keyframe. Re-encoding with every frame as a keyframe (-g 1) fixed it and because the original bitrate was so high, the scrub-optimised file came out smaller.

The first version felt frozen. I pinned the section for 480vh of scroll. It worked technically and felt broken: you scroll and nothing moves. The fix was architectural, not cosmetic make the video a sticky backdrop and let the content scroll normally over it.

Real data is messy. Degree types in the source are free text: "Master of Science", "Master of Science (MSc)", "MBA", "Certificate of participation", empty strings. Substring matching left 315 of 2,513 programs unclassified searchable, but invisible to degree filters.

Silent failures cost the most time. Three separate ones. Pydantic-settings reads .env into a settings object but never into os.environ, so os.getenv("ADMIN_PASSWORD") returned None and the admin account was never created with only a warning in the log. Pydantic's EmailStr rejects reserved TLDs, so a .local demo account failed validation before authentication ran. And in my deploy script, an || true on the Postgres firewall rule meant the call failed on every run while printing a cheerful success surfacing hours later as a 503 with a misleading "is the server running on that host?" error.

Azure had opinions. Germany West Central and West Europe both refused Postgres provisioning on my subscription; North Europe took it. Building on Apple Silicon produces arm64 images that App Service pulls and then fails to start with no useful error, so the build has to happen in Azure. And App Service containers have ephemeral storage, so ChromaDB had to move to /home or re-embed all 2,513 programs through Azure OpenAI on every restart.

Accomplishments that we're proud of

It's genuinely deployed and running, not a localhost demo real database, real vector search, real AI, a public HTTPS URL.

The eligibility breakdown is the part I care most about. Plenty of tools will recommend programs. Showing why degree, field, language, GPA, each checked separately is the difference between a suggestion and something a student can act on without hiring someone to double-check it.

And it is actually free, with no asterisk.

What we learned

Silent failure is more expensive than loud failure. Every hour I lost went to something that reported success while doing nothing a swallowed exit code, a config value that resolved to None, a warning nobody reads. I now make the critical path fail loudly and leave || true for things that genuinely don't matter.

Normalising messy real-world data is most of the work in a project like this, and it's invisible in the demo.

And pinning a page is not the same as scrollytelling. If the content doesn't move when the user scrolls, it doesn't matter how good the animation is it reads as broken.

What's next for CampusConsult

Germany went deep first, deliberately: the full application process, not a summary page. Turkey, the Netherlands, Austria, Poland, Russia, Egypt, Ukraine and Indonesia are next, added the same way.

Nearer term: fix the degree-type normalisation so those 315 programs appear in filters; harden for real traffic (the health endpoint currently over-shares, and auth logging needs stripping); replace the sample community content with real students; and put it on a proper domain so it can be shared with the people who actually need it.

How we built it

Challenges we ran into

Accomplishments that we're proud of

What we learned

What's next for CampusConsult

Built With

Share this project:

Updates

Submission history