# Yappy REST API

Base URL: **`https://yappy.biz/api/v1`**

Read endpoints are public and need no credentials. Responses are JSON
(`application/json; charset=utf-8`) and errors are RFC 9457 problem documents
(`application/problem+json`). Every response carries an `API-Version` header.

The complete contract is [`/openapi.json`](https://yappy.biz/openapi.json) —
OpenAPI 3.1, with an `operationId`, a description, and a typed response schema on
every operation, which is what makes it usable as an LLM function-calling
toolset without hand-writing wrappers.

## Discovery

```bash
curl -s https://yappy.biz/api/v1
```

Returns the endpoint index, the current API version date, the rate-limit policy,
and links to the spec, the MCP server, and the auth metadata.

## Endpoints

| Method | Path | What it returns |
|---|---|---|
| GET | `/api/v1` | Service index |
| GET | `/api/v1/releases` | Release history, newest first, paginated |
| GET | `/api/v1/releases/latest` | The current release, from the live appcast |
| GET | `/api/v1/releases/{version}` | One release by version string |
| GET | `/api/v1/docs` | Documentation index, paginated |
| GET | `/api/v1/docs/{slug}` | One documentation page, as markdown |
| GET | `/api/v1/faq` | Frequently asked questions, paginated |
| GET | `/api/v1/pricing` | Plans and backend costs |
| GET | `/api/v1/features` | Product capabilities |
| GET | `/api/v1/requirements` | System requirements |
| GET | `/api/v1/compatibility` | Whether a given Mac can run Yappy |
| GET | `/api/v1/search` | Full-text search over docs and FAQ |
| POST | `/api/v1/batch` | Up to 20 GET operations in one round trip |
| POST | `/api/v1/exports` | Start an export job (202) |
| GET | `/api/v1/exports/{job_id}` | Poll an export job |

## Pagination

List endpoints are cursor-paginated. Pass `limit` (1–100, default 20) and
`cursor`:

```bash
curl -s "https://yappy.biz/api/v1/releases?limit=2"
```

```json
{
  "object": "list",
  "data": [ { "object": "release", "version": "0.4.19" } ],
  "pagination": {
    "limit": 2,
    "has_more": true,
    "next_cursor": "cmVsZWFzZXM6Mg"
  }
}
```

`next_cursor` is opaque — pass it back verbatim as `cursor`. When `has_more` is
`false`, `next_cursor` is `null`. A `Link` header with `rel="next"` carries the
same URL for clients that prefer RFC 8288.

## Versioning and deprecation

The major version is in the path (`/api/v1`). Additive changes — new fields, new
endpoints, new enum members — ship inside `v1` without notice, so parse
leniently and ignore fields you do not know.

Breaking changes get a new major version. When a version is scheduled for
removal, every response from it carries:

```
Deprecation: true
Sunset: Wed, 01 Jul 2026 00:00:00 GMT
Link: <https://yappy.biz/developers/rest-api>; rel="deprecation"
```

with **at least 180 days** between the first `Deprecation` header and the
`Sunset` date. `v1` is current and carries neither.

The dated `API-Version` response header (for example `2026-08-25`) tells you
which build of `v1` answered.

## Batch

One round trip, up to 20 read operations:

```bash
curl -sX POST https://yappy.biz/api/v1/batch \
  -H 'content-type: application/json' \
  -H 'Idempotency-Key: 0f4c2b1a-batch-1' \
  -d '{"operations":[
        {"id":"rel","method":"GET","path":"/releases/latest"},
        {"id":"faq","method":"GET","path":"/faq?limit=2"}
      ]}'
```

Each result carries the `id` you supplied, a `status`, and a `body`. One failing
operation does not fail the batch — you get its problem document in that entry's
`body` and the rest still succeed.

## Async jobs

Exports are modelled as jobs so a slow one never blocks a request:

```bash
curl -isX POST https://yappy.biz/api/v1/exports \
  -H 'content-type: application/json' \
  -H 'Idempotency-Key: docs-export-1' \
  -d '{"resource":"docs","format":"markdown"}'
```

```
HTTP/2 202
Location: https://yappy.biz/api/v1/exports/ZG9jczptYXJrZG93bg
Retry-After: 1
```

```json
{ "object": "job", "id": "ZG9jczptYXJrZG93bg", "status": "queued",
  "resource": "docs", "poll_url": "https://yappy.biz/api/v1/exports/ZG9jczptYXJrZG93bg" }
```

Poll `poll_url` until `status` is `completed`, then read `result.url`. Statuses
are `queued`, `running`, `completed`, `failed`.

## Idempotency

Send an `Idempotency-Key` header on any `POST`. The key is echoed back on the
response and, for exports, determines the job id — so retrying a request that
timed out returns the same job rather than starting a second one.

Yappy's write operations are pure functions of their request body, so a replay
can never double-charge or duplicate a record. The header exists so your retry
logic does not have to know that.

## Conditional requests

Every `GET` returns a strong `ETag`. Send it back as `If-None-Match` and you get
a `304 Not Modified` with no body, which is the cheapest way to poll for a new
release.

## Rate limits and errors

See [rate limits](https://yappy.biz/developers/rate-limits) and
[errors](https://yappy.biz/developers/errors).

---

Canonical: https://yappy.biz/developers/rest-api/ · Last updated: 2026-08-25
