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

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

MethodPathWhat it returns
GET/api/v1Service index
GET/api/v1/releasesRelease history, newest first, paginated
GET/api/v1/releases/latestThe current release, from the live appcast
GET/api/v1/releases/{version}One release by version string
GET/api/v1/docsDocumentation index, paginated
GET/api/v1/docs/{slug}One documentation page, as markdown
GET/api/v1/faqFrequently asked questions, paginated
GET/api/v1/pricingPlans and backend costs
GET/api/v1/featuresProduct capabilities
GET/api/v1/requirementsSystem requirements
GET/api/v1/compatibilityWhether a given Mac can run Yappy
GET/api/v1/searchFull-text search over docs and FAQ
POST/api/v1/batchUp to 20 GET operations in one round trip
POST/api/v1/exportsStart 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:

curl -s "https://yappy.biz/api/v1/releases?limit=2"
{
  "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:

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:

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
{ "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 and errors.

Reading this as an agent? The same page in markdown: /developers/rest-api.md · machine index: /llms.txt · API: /openapi.json · MCP: https://yappy.biz/mcp