# saileshdahal.com.np developer API

> A free, read-only JSON API over Sailesh Dahal's published writing. No keys, no signup, no sandbox to configure: the production API is open by design.

- Canonical: https://saileshdahal.com.np/developers
- OpenAPI spec: https://saileshdahal.com.np/openapi.json
- API root: https://saileshdahal.com.np/api/v1
- Contact: hi@saileshdahal.com.np

---

## Quickstart

```sh
# List every post as JSON
curl https://saileshdahal.com.np/api/v1/posts

# One post, metadata
curl https://saileshdahal.com.np/api/v1/posts/flavor-setup-flutter

# The same post as clean markdown
curl https://saileshdahal.com.np/api/v1/posts/flavor-setup-flutter/markdown

# Keyword search
curl "https://saileshdahal.com.np/api/v1/search?q=flutter+flavors"
```

## Authentication

None. Every read endpoint is public. There are no API keys because there is nothing to gate: the data is the blog itself. Do not POST to /api/contact programmatically; it is a human form and rejects scripted submissions.

## Versioning and deprecation policy

Versioned by URL path: `/api/v1`. Additions are non-breaking; breaking changes ship only under `/api/v2`, never inside `/v1`.

Deprecations are announced at least 12 months ahead, signalled on every response with the RFC 9745 `Deprecation` header followed by an RFC 8594 `Sunset` header carrying the removal date. Neither header is sent today because nothing is deprecated.

## Errors

Every 4xx and 5xx from /api/* is RFC 9457 `application/problem+json`: a stable machine-readable `code`, a human-readable `title` and `detail`, the HTTP `status`, and usually a `hint` naming what to try next. Codes currently defined: `not_found`, `method_not_allowed`, `bad_request` variants (`invalid_slug`, `missing_query`, `unsupported_filter`, `invalid_form`), `validation_failed`, `origin_not_allowed`, `rate_limited`, `delivery_failed`, `internal_error`, `not_acceptable`.

Example:

```json
{
  "type": "https://saileshdahal.com.np/developers#error-not_found",
  "title": "Not Found",
  "status": 404,
  "code": "not_found",
  "detail": "No post with slug \"nope\".",
  "hint": "Call GET /api/v1/posts for valid slugs."
}
```

## Rate limits

Reads carry a `RateLimit-Policy` header (draft-ietf-httpapi-ratelimit-headers-11 format) describing advisory fair use of roughly 600 requests per minute per client. It is guidance, not enforcement: reads are not hard-throttled today, so self-throttle to the advertised policy anyway.

Only one limit is enforced: `POST /api/contact` allows 5 submissions per minute per IP and answers excesses with `429` plus `Retry-After: 60`.

## Endpoints

| Method | Path | Purpose |
|---|---|---|
| GET | `/api/v1` | Service index: endpoint list, spec link |
| GET | `/api/v1/posts?tag={tag}` | All posts as JSON; optional tag filter |
| GET | `/api/v1/posts/{slug}` | One post as JSON; `Accept: text/markdown` returns markdown |
| GET | `/api/v1/posts/{slug}/markdown` | One post as raw markdown |
| GET | `/api/v1/search?q={query}` | Keyword search over titles, tags, summaries |
| POST | `/api/mcp` | MCP server (Streamable HTTP), read-only tools |
| POST | `/api/contact` | Human contact form; not for agents |

The full contract, including typed response schemas and every error shape, is the OpenAPI document: [openapi.json](/openapi.json).

## Related surfaces

- [llms.txt](https://saileshdahal.com.np/llms.txt): agent index of every page
- [llms-full.txt](https://saileshdahal.com.np/llms-full.txt): the whole corpus in one file
- [feed.json](https://saileshdahal.com.np/feed.json): JSON Feed 1.1 index
- Markdown twins: append .md to any page URL
- [MCP server card](https://saileshdahal.com.np/.well-known/mcp/server-card.json)
- [Agent card (A2A)](https://saileshdahal.com.np/.well-known/agent-card.json)
