← Back to portfolio

Meducate API

Medical Data REST API — Case Study

meducateapi.com — currently under development
Dotnet 10Blazor ServerPostgreSQLSemantic KernelHangfireRailwayWHO ICD-11 API

The Problem

This started as an idea about how to use AI without opening myself up to hallucination risk. That eventually evolved into a generic structured data pipeline, one that didn't have to be about medical data specifically, medical was just the domain I picked for the challenge (and partly because there's so much unstructured data floating around in it). MedlinePlus (the National Library of Medicine's consumer health site, 2,000+ topics) and PubMed are a good example of the problem: each one uses its own formats, its own update schedule, and its own access method. Look up something as common as Type 2 Diabetes and you'll find conflicting field names, missing symptoms, or treatment info that's years out of date, depending on which provider you asked. Every developer building a health education tool ends up writing the same brittle scraper and hand-rolled parser, and it breaks the moment an upstream source changes anything. I wanted to solve that once, at the infrastructure level, rather than rebuild it for every project I picked up.

Architecture & Tech Decisions

Meducate is built with Clean Architecture across four layers: Domain, Application, Infrastructure, and Presentation. That separation means I can swap out a data provider, the LLM processor, or the persistence layer without the others caring. Ironically, it's the one part of this project I'd probably do differently now. Since building this I've moved onto a hybridised vertical-slice architecture at my full-time job, and it's a drastic improvement when you're working alongside an AI coding partner. Clean Architecture wasn't the wrong call at the time, it's just not what I'd reach for today. It still runs as a single Dotnet 10 monolith on Railway, which keeps things simple while the product is early.

LLM ingestion pipeline

Two Hangfire jobs handle ingestion. A TopicDiscoveryJob runs at 2 AM UTC and pulls new topics from MedlinePlus and PubMed. From there, an LLM classification step (Semantic Kernel on top of OpenAI GPT-4) assigns each topic one of 26 standardised medical categories and a type: disease, drug, procedure, symptom, and so on. A second pass extracts the structured fields, summary, symptoms, causes, treatments, citations, then a quality-control step screens for hallucinations, merges synonyms, and validates everything before it is saved. Existing topics get reprocessed at 3 AM UTC so nothing goes stale. Strip away the detail and it's really just four steps: ingest, format, screen for hallucinations, serve. That's the part of this project I'm most proud of, it's a generic enough pattern that it isn't really tied to medical data at all, it would work for pretty much any domain with a lot of messy source material. I went with Semantic Kernel mainly because it, and Blazor, sit inside the Dotnet ecosystem I already use day to day at my full-time job, rather than juggling a second stack purely for this project.

WHO ICD-11 coding

Diagnosable topics, diseases, disorders, syndromes, symptoms, mental health conditions, get matched against the official WHO ICD-11 API and tagged with a real diagnostic code and title. A Hangfire step authenticates against WHO's OAuth token endpoint, searches the ICD-11 MMS release, and only accepts the first codeable entity in the pre-sorted result set, deliberately conservative, since a wrong code is worse than no code at all. About 88% of eligible topics match automatically; the rest are names too colloquial or broad for WHO's exact-match search, and get retried on every refresh cycle rather than forced.

Blazor Server dashboard

The developer portal is Blazor Server with passwordless magic-link authentication (emails go out through the Resend API) and cookie-based sessions. From there you create an organisation, generate up to five API keys, and watch usage on a live dashboard. Keeping the front end in Blazor kept the whole stack in C#, with models shared between the API and the UI instead of duplicated in TypeScript.

PostgreSQL + EF Core

The normalised medical data, user accounts, organisations, and usage metrics all live in PostgreSQL 16 through Entity Framework Core and Npgsql. Hangfire also persists to PostgreSQL, so a restart does not lose a job mid-run, and anything that fails can be retried straight from Hangfire's own dashboard.

Minimal API surface

The public API is intentionally small: /api/topics, /api/topics/search, and /api/topics/{name}, all behind an API key passed as an X-Api-Key header. Rate limiting is two-tier, 60 requests a minute per key plus a configurable daily cap, with an alert at 80% usage. What comes back depends on the topic type: a disease returns symptoms, causes, and treatments, while a symptom returns related symptoms and associated conditions instead. If a topic disappears from every upstream source, it gets pulled from the API automatically rather than sitting around stale.

Outcome

At its core, Meducate is a hub that lets developers fetch auto-updated medical data for free, pulled from multiple trusted sources and formatted into a predictable structure you can query programmatically. Right now it's ingesting and classifying over 2,000 health topics from MedlinePlus and PubMed, each with a structured summary, symptoms, causes, treatments, and citations, sorted into 26 standardised medical categories and refreshed every day without me touching anything. Roughly 88% of diagnosable topics also carry a verified WHO ICD-11 code. Getting access is meant to be quick: request a magic link, create an organisation, generate a key, and you're querying structured medical data within a few minutes. The core pipeline is done, it's really the formatting and accessibility layer that's finished. What's next is webhooks, more data sources, and generally better accessibility options for developers pulling from it.