This site is built to be read by people and by software. Everything on this page is public, unauthenticated, and read-only: there is no API key, no signup, and no SDK to install. It works with plain HTTP, with OpenAPI-aware clients, and with agents that call tools from a function schema.
Base URL
All endpoints are relative to https://www.joelcsaji.com and answer over HTTPS only.
Endpoints
| Method | Path | Operation | What it returns |
|---|---|---|---|
| GET | /api/posts | listPosts | List published articles |
| GET | /api/posts/{slug} | getPost | Get one article |
| GET | /api/projects | listProjects | List portfolio projects |
| GET | /api/projects/{id} | getProject | Get one project |
| GET | /api/now | getNow | Get the /now snapshot |
| GET | /api/ai-terms | searchAiTerms | Search the AI dictionary |
| GET | /api/ai-terms/{slug} | getAiTerm | Get one dictionary entry |
| GET | /api/search-index | getSearchIndex | Get the site search index |
The full contract, including parameter and response schemas, is the OpenAPI 3.1 document at /openapi.json. Every operation has a unique operationId, a description, typed parameters, and typed responses, so the document can be loaded directly into function-calling toolchains, API clients, or code generators.
Quickstart
# The newest article
curl -s https://www.joelcsaji.com/api/posts | jq '.items[0]'
# One article, including its Markdown body
curl -s https://www.joelcsaji.com/api/posts/react-19-deep-dive-server-components-compiler-hooks
# Search the AI dictionary
curl -s 'https://www.joelcsaji.com/api/ai-terms?q=agent&maturity=Rising'
# Portfolio projects and case studies
curl -s https://www.joelcsaji.com/api/projects
# The site search index behind the command palette
curl -s https://www.joelcsaji.com/api/search-index
# The current now-page snapshot
curl -s https://www.joelcsaji.com/api/now
# The machine-readable contract
curl -s https://www.joelcsaji.com/openapi.json
List responses have the shape { "items": [...], "total": n }, where n counts everything the filters matched rather than just this page, so a paging client can read total and totalPages without guessing. Single resources are returned as a bare object. Every JSON response carries Access-Control-Allow-Origin: *, so browser-based tools can call the API directly.
Markdown for every page
Every HTML page on this site is also available as Markdown. Send Accept: text/markdown, or fetch the same URL with .md appended, and the response is the page's content without navigation, styles, or scripts:
curl -H 'Accept: text/markdown' https://www.joelcsaji.com/about
curl -s https://www.joelcsaji.com/about.md
curl -H 'Accept: text/markdown' https://www.joelcsaji.com/blog/react-19-deep-dive-server-components-compiler-hooks
Markdown responses use Content-Type: text/markdown; charset=utf-8. Every page response, HTML or Markdown, carries Vary: Accept so caches keep the two apart. Quality values are honoured (text/markdown;q=0.5, text/html still returns HTML), and a client that accepts neither HTML nor Markdown receives 406 Not Acceptable.
Rate limits
Each client may make 60 API requests per 60 seconds. Every API response reports the quota using the IETF RateLimit header fields:
RateLimit-Policy: "default";q=60;w=60
RateLimit: "default";r=59;t=60
q is the quota, w the window in seconds, r the remaining requests, and t the seconds until the window resets. Once the quota is exhausted the API answers 429 Too Many Requests with a Retry-After header in seconds; wait that long and retry. Limits apply per client and are not tied to any account.
Errors
Errors are JSON with the same shape everywhere, including unknown /api/* paths and unsupported methods:
{
"error": {
"code": "not_found",
"message": "Post \"nope\" was not found.",
"hint": "List the available items at https://www.joelcsaji.com/api/posts."
}
}
| Status | code | When |
|---|---|---|
| 400 | invalid_parameter | A query parameter has an unsupported value; the hint lists accepted values. |
| 404 | not_found | No item matches the identifier, or no endpoint exists at the path. |
| 405 | method_not_allowed | Only GET and HEAD are supported. |
| 429 | rate_limited | The quota above was exceeded; honour Retry-After. |
| 500 | internal_error | Something failed on the server; retry later. |
Machine-readable files
- /llms.txt: a summary of the site for language models, with guidance on when to use it.
- /llms-full.txt: the about page, every portfolio case study, and all articles as one Markdown document.
- /openapi.json: the OpenAPI 3.1 contract for the JSON API.
- /sitemap.xml: every public URL, with a
lastmoddate where the content has a real one. - /rss.xml: new articles as they publish.
- /feed.json: the same articles as a JSON Feed 1.1 document.
- /robots.txt: crawler guidance. Search engines, AI assistants, and agents are welcome.
- /.well-known/ard.json: the Agentic Resource Discovery catalog listing these endpoints.
- /.well-known/security.txt: how to report a security issue.
Attribution and contact
Content is written by Joel Saji. Cite the canonical URL of the page you drew from. There is no official CLI or client library; the API is small enough to call directly. Questions, broken responses, or ideas for the API: email hello@joelcsaji.com or use the contact page.