API
Everything on this site is data first. These endpoints return the same records the pages are built from. All of them are static JSON generated at build time, except /api/ask, which turns a question into SQL, /api/questions, which passes on a question a visitor chose to send, and /api/stats, which reports the counters. Rate limits: 10 questions a minute per address, shared by /api/ask and /api/questions, and a monthly cap; when the cap is reached the endpoint returns 503 with reason "budget".
A question to /api/ask is cached for a month by its wording, so repeat questions are free. The monthly cap counts model calls, not questions, in the UTC calendar month.
Try it:
curl https://alexkachur.com/api/projects.jsonThe whole API as an OpenAPI 3.1 document, built from the same schema and endpoint list, for loading into any client: /api/openapi.json
GET /api/projects.json
The projects on this site, in site order
curl https://alexkachur.com/api/projects.json[
{
"id": 1,
"slug": "splitroof-ai-assistant",
"name": "SplitRoof AI assistant",
"kind": "team",
"summary": "An assistant inside a shared-household expense app that answers question...",
"role": "AI integration, documentation, schedule",
"year_start": 2026,
"year_end": null,
"client_name": null,
"live_url": null,
"repo_url": "https://github.com/AlexKachur98/splitroof-assistant-spike",
"has_live_demo": 0,
"uses_llm": 1,
"llm_job": "answers questions about a household's shared expenses by calling typed t...",
"paid": 0,
"featured": 1,
"card": "A read-only assistant that answers \"who owes what this month\" from a hou...",
"team": "group project, COMP 231 Software Development Project, Fall 2026",
"highlights": "Own the assistant for a shared-household expense app: a read-only agent ..."
},
...
]GET /api/projects/{slug}.json
curl https://alexkachur.com/api/projects/splitroof-ai-assistant.json{
"id": 1,
"slug": "splitroof-ai-assistant",
"name": "SplitRoof AI assistant",
"kind": "team",
"summary": "An assistant inside a shared-household expense app that answers question...",
"role": "AI integration, documentation, schedule",
"year_start": 2026,
"year_end": null,
"client_name": null,
"live_url": null,
"repo_url": "https://github.com/AlexKachur98/splitroof-assistant-spike",
"has_live_demo": 0,
"uses_llm": 1,
"llm_job": "answers questions about a household's shared expenses by calling typed t...",
"paid": 0,
"featured": 1,
"card": "A read-only assistant that answers \"who owes what this month\" from a hou...",
"team": "group project, COMP 231 Software Development Project, Fall 2026",
"highlights": "Own the assistant for a shared-household expense app: a read-only agent ...",
"technologies": [
"Anthropic API",
"Express",
...
]
}GET /api/facts.json
Single facts about Alex, one per row
curl https://alexkachur.com/api/facts.json[
{
"key": "available_from",
"value": "2027-05",
"description": "The month Alex can start full-time work, as YYYY-MM"
},
{
"key": "email",
"value": "alexkachur98@gmail.com",
"description": "Alex's email address"
},
...
]GET /api/technologies.json
Languages, frameworks, libraries and services Alex has used
curl https://alexkachur.com/api/technologies.json[
{
"id": 1,
"name": ".NET",
"category": "framework",
"core": 0,
"skill_area": "Backend"
},
{
"id": 2,
"name": "Anthropic API",
"category": "ai",
"core": 1,
"skill_area": "LLM integration"
},
{
"id": 3,
"name": "Astro",
"category": "framework",
"core": 0,
"skill_area": "Frontend"
},
...
]GET /api/project_images.json
The screenshots on each case study, in display order
curl https://alexkachur.com/api/project_images.json[
{
"project_id": 1,
"position": 1,
"alt": "A terminal in VS Code after npm test in splitroof-assistant-spike, with ...",
"caption": null
},
{
"project_id": 1,
"position": 2,
"alt": "VS Code with src/assistant.js open on the system prompt, which tells the...",
"caption": null
},
...
]GET /api/courses.json
Courses Alex is taking at Centennial College
curl https://alexkachur.com/api/courses.json[
{
"code": "COMP 231",
"name": "Software Development Project 1",
"term": "Fall 2026",
"topics": "A team project taken from requirements to a working, tested and document..."
},
{
"code": "COMP 255",
"name": "Business and Entrepreneurship for SET",
"term": "Fall 2026",
"topics": "How businesses are organised and run, ethics and privacy, and turning so..."
},
...
]GET /api/timeline.json
Work, education and project events by date
curl https://alexkachur.com/api/timeline.json[
{
"id": 1,
"date": "2019-08",
"kind": "work",
"event": "Manager, IOAL Distributing"
},
{
"id": 2,
"date": "2022-01",
"kind": "work",
"event": "QA Tester, 360 Plus IT Consulting"
},
...
]GET /api/pets.json
The cats Alex lives with
curl https://alexkachur.com/api/pets.json[
{
"name": "Moura",
"species": "cat",
"breed": "Norwegian Forest cat",
"born": 2014,
"personality": "makes you earn her love, then becomes your best friend",
"photo_url": "/images/pets/moura.webp",
"photo_alt": "Close-up of Moura on the cat tree, pink nose forward"
},
...
]GET /api/experience.json
Jobs and roles Alex has held
curl https://alexkachur.com/api/experience.json[
{
"id": 1,
"title": "Manager",
"organization": "IOAL Distributing",
"location": "Mississauga, ON",
"start": "2019-08",
"end": "2022-01",
"summary": "Warehouse operations.",
"highlights": "Ran daily warehouse operations (inventory control, order fulfilment, ven..."
},
...
]GET /api/interests.json
Alex's hobbies and favourites, in his words
curl https://alexkachur.com/api/interests.json[
{
"id": 1,
"category": "video game",
"name": "Counter-Strike",
"note": "My competitive game. I was the in-game leader (IGL) for a semi-pro team ..."
},
{
"id": 2,
"category": "video game",
"name": "The Elder Scrolls V: Skyrim",
"note": "Rich characters and stories, and endless ways to play. Around 3,000 hour..."
},
...
]GET /api/storage.json
What this site stores for a visitor or a question, how long, and why, from the constants the code uses
curl https://alexkachur.com/api/storage.json[
{
"item": "The SQL and the model's one-line explanation for a question, which can r...",
"kept_for": "30 days",
"purpose": "Repeat questions are answered from the cache, free"
},
{
"item": "The model's reason a question cannot be answered, which can repeat words...",
"kept_for": "1 day",
"purpose": "The same, for questions the site cannot answer"
},
...
]GET /api/uses.json
The machines, peripherals and software Alex uses, and what he is learning, as listed on /uses
curl https://alexkachur.com/api/uses.json[
{
"position": 1,
"section": "Machines",
"item": "Main PC",
"details": "ASUS ROG Astral GeForce RTX 5090, AMD Ryzen 7 9850X3D, ASUS ROG Strix X8..."
},
{
"position": 2,
"section": "Machines",
"item": "Second PC",
"details": "GeForce RTX 3080, Intel Core i9-12900K. Also built myself; now mostly fo..."
},
...
]GET /api/sections.json
The text of the site's pages and case studies, one row per section
curl https://alexkachur.com/api/sections.json[
{
"page": "/#about",
"position": 1,
"heading": "About",
"body": "I build full-stack web apps, and I like adding AI that solves real-world..."
},
...
]GET /api/page_images.json
The photos on the site's other pages
curl https://alexkachur.com/api/page_images.json[
{
"page": "/#about",
"position": 1,
"alt": "Moura on the top bed of a cat tree and Simba on the lower one, in window...",
"caption": "Moura takes the top bed and Simba takes what is left."
},
{
"page": "/404",
"position": 1,
"alt": "Simba mid-meow on a patterned rug, mouth wide open",
"caption": "Simba, reacting to the URL you typed."
}
]GET /api/schema.json
curl https://alexkachur.com/api/schema.json{
"hash": "4b6fe712744e9660a177de49ea027df5103669b55f213f6132e743eb5813...",
"ddl": "-- Single facts about Alex, one per row\nCREATE TABLE facts (...",
"tables": [
{
"name": "facts",
"description": "Single facts about Alex, one per row",
"primaryKey": [
"key"
],
"columns": [
{
"name": "key",
"type": "TEXT",
"nullable": false,
"unique": false,
"description": "Fact name, for example location or available_from"
},
...
]
},
...
],
"factKeys": [
"available_from",
...
],
"facts": [
{
"key": "available_from",
"description": "The month Alex can start full-time work, as YYYY-MM"
},
...
],
"sectionPages": [
"/#about",
...
],
"sectionHeadings": [
"About",
...
],
"photoAlt": {
"/images/pets/moura.webp": "Close-up of Moura on the cat tree, pink nose forward",
"/images/pets/simba.webp": "Simba peeking out from between grey couch cushions"
}
}GET /api/resume.json
Alex's resume in the JSON Resume format, built from the same tables.
curl https://alexkachur.com/api/resume.json{
"basics": {
"name": "Alex Kachur",
"label": "Full-stack & AI developer",
"email": "alexkachur98@gmail.com",
"url": "https://alexkachur.com",
"summary": "I build web apps with an AI doing real work inside them.",
"location": {
"city": "Toronto"
},
"profiles": [
{
"network": "GitHub",
"username": "AlexKachur98",
"url": "https://github.com/AlexKachur98"
},
...
]
},
"work": [
{
"name": "Self-employed",
"position": "Freelance Web Developer",
"location": "Thornhill, ON",
"startDate": "2026",
"summary": "Uraz Hoops (urazhoops.com); Think Smarter Insurance review f...",
"highlights": [
"Built and handed off a five-page marketing site for Uraz Hoo...",
...
]
},
...
],
"education": [
{
"institution": "Centennial College",
"area": "Software Engineering Technology",
"studyType": "Advanced Diploma",
"startDate": "2025-01",
"endDate": "2027-04",
"score": "4.4 / 4.5",
"courses": [
"COMP 231 Software Development Project 1",
...
]
}
],
"skills": [
{
"name": "Languages",
"keywords": [
"SQL",
...
]
},
...
],
"languages": [
{
"language": "English",
"fluency": "Fluent"
},
...
],
"projects": [
{
"name": "SplitRoof AI assistant",
"description": "An assistant inside a shared-household expense app that answ...",
"highlights": [
"Own the assistant for a shared-household expense app: a read...",
...
],
"keywords": [
"Anthropic API",
...
],
"startDate": "2026",
"roles": [
"AI integration, documentation, schedule"
],
"url": "https://github.com/AlexKachur98/splitroof-assistant-spike"
},
...
],
"interests": [
{
"name": "video game",
"keywords": [
"Counter-Strike",
...
]
},
...
]
}POST /api/ask
curl -X POST https://alexkachur.com/api/ask -H 'content-type: application/json' -d '{"question":"What has Alex built for paying clients?"}'Each answer also carries a token: to send that question to Alex, pass it to /api/questions within 10 minutes.
What is stored
SELECT item, kept_for, purpose FROM storage;| item | kept_for | purpose |
|---|---|---|
| The SQL and the model's one-line explanation for a question, which can repeat words from it, under a SHA-256 hash of the question | 30 days | Repeat questions are answered from the cache, free |
| The model's reason a question cannot be answered, which can repeat words from it, under a SHA-256 hash of the question | 1 day | The same, for questions the site cannot answer |
| A counter keyed by a scrambled form of your address | 2 minutes 1 second | The rate limit, for questions and sent questions alike |
| Two monthly totals: questions answered and model calls | 40 days | The footer and the monthly cap |
| A question you chose to send to Alex, and the day you sent it | 90 days | So Alex can add what is missing |
| The number of questions sent today | 2 days | The daily cap on sent questions |
| Your light or dark theme choice, in your own browser, never sent to this site | until you clear it | Keeps the theme you picked across pages |
Everything in the table but the theme choice is held in Upstash Redis.
Your address is scrambled with a keyed hash (HMAC-SHA-256) before anything is stored: a plain hash of an address could be reversed by hashing every address in turn, but without the site's secret key the stored value cannot be matched back to an address.
One line per failed request: status, error type, request ID and timing, written to Vercel's logs. Never the question or your address.
Anthropic, which runs the model, receives the question and, under its data retention policy, deletes API inputs and outputs within 30 days, or keeps them up to 2 years if its safety systems flag a request; it does not train on them, since this site's account has not opted in to share data.
Vercel, which hosts the site, keeps its request logs, that failed-request line included, for one hour on this site's plan, and counts page views for Web Analytics without cookies, discarding the visitor hash after 24 hours.
POST /api/questions
Stores a question the site could not answer, for Alex to read, only when the visitor chooses to send it. It needs the token /api/ask returned with that question, no older than 10 minutes, and keeps only the question and the day it was sent, for 90 days.
curl -X POST https://alexkachur.com/api/questions -H 'content-type: application/json' -d '{"question":"A question the site could not answer","token":"<token from /api/ask>"}'{
"sent": true
}GET /api/stats
curl https://alexkachur.com/api/stats