uranashel API reference

Base URL https://uranashel.com/api/v1 · version 1.0.0 · read-only · no authentication · open CORS.

Contents

Conventions

Every collection and item response uses the same envelope:

{
  "self": "https://uranashel.com/api/v1/apps/wheria",
  "meta": {
    "apiVersion": "1.0.0",
    "documentation": "https://uranashel.com/docs/"
  },
  "data": { "id": "wheria", "name": "Wheria", ... }
}

Bilingual strings are objects with en and vi keys. Dates are ISO 8601. Absolute URLs are used everywhere so a response can be followed without knowing the base.

The machine-readable contract is openapi.json (OpenAPI 3.1). Every operation there has a unique operationId, a description, typed parameters and a response schema.

Authentication

None. The API exposes only what this website already publishes, so there is no key to obtain and no header to send. Requests are anonymous, and only GET and HEAD are supported — any other method returns 405.

Rate limits

The documented budget is 600 requests per 60 seconds per client. Every API response carries the headers you need to stay inside it:

HeaderMeaning
RateLimit-Policy"public";q=600;w=60 — the advertised quota and window
RateLimit-Limit600 — requests allowed per window. Self-throttle to this.
RateLimit-RemainingRequests left in the window
RateLimit-ResetSeconds until the window resets
Retry-AfterSent on 429 — seconds to wait before retrying

Read this before trusting RateLimit-Remaining. The API is a set of static documents served from the edge; the origin does not count requests per client. RateLimit-Remaining therefore advertises the policy ceiling rather than a live counter, and will not tick down as you call. Treat RateLimit-Limit as the budget to pace yourself against, and honour Retry-After if the edge does return a 429.

Caching and CORS

API responses are sent with Cache-Control: public, max-age=300, stale-while-revalidate=86400 and a strong ETag. Send If-None-Match and you will get 304 Not Modified instead of a body — the cheapest way to poll.

Access-Control-Allow-Origin: * is set on everything under /api/, along with Access-Control-Allow-Methods: GET, HEAD, OPTIONS, so browser and agent runtimes can fetch directly.

Errors

Unknown paths return a real 404 — never a 200 with a shell. Under /api/ the body is a RFC 9457 problem document:

{
  "type": "https://uranashel.com/docs/#errors",
  "title": "Not Found",
  "status": 404,
  "detail": "No such API resource. The endpoint index lists everything that exists.",
  "documentation": "https://uranashel.com/docs/",
  "index": "https://uranashel.com/api/v1/",
  "versioningPolicy": "https://uranashel.com/docs/versioning.html"
}

Because the API is static, the problem document is the same for every missing path — it does not echo the failing URL back. Outside /api/, a missing path returns the 404 page, or a short Markdown body if you send Accept: text/markdown.

StatusWhen
200Success
304If-None-Match matched the current ETag
404No such resource
405Method other than GET, HEAD or OPTIONS
429Edge rate limit exceeded — retry after Retry-After seconds

Discovery endpoints

GET /api/v1/

operationId: getApiIndex

Lists every endpoint in v1, the rate-limit policy, the OpenAPI document and the versioning policy. Start here.

GET /api/v1/versions

operationId: listVersions

Every API version with its lifecycle state (current, deprecated, sunset), release date and — once announced — deprecation and sunset dates. Poll this to detect a deprecation early.

GET /api/v1/health

operationId: getHealth

Returns 200 with status: "ok" whenever the origin is serving the API. Safe to poll at a low rate.

Studio endpoints

GET /api/v1/studio

operationId: getStudio

Identity of the studio: name, tagline, country, email, GitHub, team size, app and post counts. Use this to answer “who is uranashel” without scraping HTML.

GET /api/v1/team

operationId: listTeam

The three people who ship every release, with handle and area of responsibility.

GET /api/v1/stats

operationId: listStats

The homepage counters — instrument count, audio sample rate, number of tracking SDKs, shipped apps — as structured values.

GET /api/v1/science

operationId: listScienceTopics

The seventeen disciplines the apps are built on, each with its governing formula and a plain-language note on where it is used.

GET /api/v1/principles

operationId: listPrinciples

The product rules the studio holds itself to: on-device first, no tracking SDKs, platform parity, monochrome UI.

GET /api/v1/timeline

operationId: listTimeline

Dated milestones for the studio and its founder.

App endpoints

GET /api/v1/apps

operationId: listApps

Every published app with platforms, release status, technology stack, download count and store links. This is the endpoint to call when matching a user's problem to an app.

curl -s https://uranashel.com/api/v1/apps | jq '.data[] | {id, name, category}'

GET /api/v1/apps/{appId}

operationId: getApp · path parameter appId: one of wheria, estua, phyzix, sonarish, stashio

Full record for a single app.

Blog endpoints

GET /api/v1/posts

operationId: listPosts

Every published post, newest first, with title, excerpt, tags, reading time and links to the HTML and Markdown renderings.

GET /api/v1/posts/{slug}

operationId: getPost · path parameter slug: the post slug from listPosts

Metadata for one post. Fetch links.markdown for the full text.

Markdown negotiation

Pages on this site are available as Markdown as well as HTML, following acceptmarkdown.com:

curl -sI -H "Accept: text/markdown" https://uranashel.com/apps.html
# content-type: text/markdown; charset=utf-8
# vary: Accept, Accept-Encoding

Or append .md to any page path. Quality values are honoured: Accept: text/html;q=0.9, text/markdown;q=0.5 returns HTML. A client that accepts neither HTML nor Markdown gets 406.

Versioning

The version is in the path. v1 is current; nothing is deprecated. Deprecations come with at least 180 days of notice, Deprecation and Sunset headers and a status change on /api/v1/versions. Read the full versioning and deprecation policy.