Documentation # uranashel API reference Base URL `https://uranashel.com/api/v1` · version 1.0.0 · read-only · no authentication · open CORS. #### Contents - [Conventions](#conventions) - [Authentication](#auth) - [Rate limits](#rate-limits) - [Caching and CORS](#caching) - [Errors](#errors) - [Discovery endpoints](#discovery) - [Studio endpoints](#studio-endpoints) - [App endpoints](#app-endpoints) - [Blog endpoints](#blog-endpoints) - [Markdown negotiation](#markdown) - [Versioning](#versioning) ## 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](https://uranashel.com/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: | Header | Meaning | | --- | --- | | `RateLimit-Policy` | `"public";q=600;w=60` — the advertised quota and window | | `RateLimit-Limit` | `600` — requests allowed per window. **Self-throttle to this.** | | `RateLimit-Remaining` | Requests left in the window | | `RateLimit-Reset` | Seconds until the window resets | | `Retry-After` | Sent 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](https://www.rfc-editor.org/rfc/rfc9457) 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](https://uranashel.com/404.html), or a short Markdown body if you send `Accept: text/markdown`. | Status | When | | --- | --- | | `200` | Success | | `304` | `If-None-Match` matched the current `ETag` | | `404` | No such resource | | `405` | Method other than `GET`, `HEAD` or `OPTIONS` | | `429` | Edge 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](https://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`](https://uranashel.com/api/v1/versions). Read the full [versioning and deprecation policy](https://uranashel.com/versioning.html). --- uranashel · [Home](https://uranashel.com/) · [Apps](https://uranashel.com/apps.html) · [Lab](https://uranashel.com/lab.html) · [About](https://uranashel.com/about.html) · [Blog](https://uranashel.com/blog/) · [Developers](https://uranashel.com/developers/) · [API docs](https://uranashel.com/docs/) · [Privacy](https://uranashel.com/privacy.html) Machine-readable: [llms.txt](https://uranashel.com/llms.txt) · [sitemap.xml](https://uranashel.com/sitemap.xml) · [openapi.json](https://uranashel.com/openapi.json) · [API](https://uranashel.com/api/v1/)