← Back to work
↗ Code Email
Sunset · 2025 Case study

College Essay Prompts API

Admissions essay prompts as one clean request, not a hunt across a hundred university sites. The API serves roughly 1,700 prompts behind a single endpoint.

~1,700
prompts packaged
500
accounts before sunset
269–1006
ms response by size
1
request, not a PDF hunt
Role — Sole engineer Stack — FastAPI · Python · Selenium · MongoDB
01 — The problem

Everyone needed the data as data. Instead, it sat in PDFs.

Counselors, advising firms, and student tools all wanted admissions essay prompts in a structured, queryable form. Instead, the prompts sat scattered across a hundred university admissions pages, in inconsistent formats, changing every cycle. Every tool needing them ran its own scraper against the same sites.

02 — Architecture

Scrape once, normalize, serve many.

A Selenium collection layer gathered prompts from source sites into a normalized MongoDB collection. A FastAPI service served them with filtering and API-key auth, so consumers made one request instead of running their own scrapers.

Admissions sites
~100 sources
Selenium
collect + normalize
MongoDB
~1,700 prompts
FastAPI
key auth · filters

Collection ran on a schedule. The API served a clean, cached collection.

03 — The endpoint

One filtered, key-authed request.

api/prompts.py
# GET /prompts?school=...&year=... : gated by API key
@app.get("/prompts")
async def get_prompts(
    school: str | None = None,
    year: int | None = None,
    key: str = Depends(verify_api_key),
):
    q: dict = {}
    if school: q["school"] = {"$regex": school, "$options": "i"}
    if year:   q["year"] = year
    docs = await db.prompts.find(q).to_list(length=2000)
    return {"count": len(docs), "prompts": docs}

Illustrative excerpt. See the repo for the real implementation.

04 — Outcome

Reached 500 accounts, then sunset on purpose.

The API grew to 500 accounts, with response times between 269 and 1,006 ms depending on request size. The team sunset the API on purpose. Upstream data-collection costs outgrew what the experiment was worth. This was a call to stop, not a failure.

500
Accounts served before sunset
~1,700
Prompts behind one endpoint
Sub-1s
Typical response time
Read the code, or see the rest of my work.

Source is archived on GitHub.

↗ View on GitHub ← All work