acyka api

one contract · six libraries · a playground

The catalogue, and one account's own things,
behind a token.

43 operations over the anime catalogue, public profiles, and the lists, shelves and writing of whoever authorised your application. Every one of them is the same function acyka's own site calls, so what you read is what it reads.

looking things up
import { Acyka } from '@acyka/api';

// a bot has nobody to sign in, so it acts for itself
const acyka = Acyka.app({ clientId, clientSecret });

const found = await acyka.catalogue.listTitles({ q: 'frieren', limit: 5 });
//    ^? Page<TitleCard>

for await (const title of acyka.catalogue.listTitlesAll({ genre: 'Drama' })) {
  console.log(title.title, title.year);
}
acting for a person
// and acting for a person, with a token that renews itself
const theirs = Acyka.user({ clientId, clientSecret }, tokens, save);

const me = await theirs.account.getMe();
await theirs.library.saveListEntry({
  shikimori_id: 52991,
  body: { title: 'Sousou no Frieren', status: 'watching', episode: 4 }
});

Generated from the server itself

openapi.json is written by the api from annotations that sit on its own handlers, and a test there drives every path in it against the real router. A route that changes shape changes the libraries in the same release, and a path that does not exist cannot be described.

Six libraries, one reader

The types and the methods are generated; the transport, the four OAuth flows, the token that renews itself, the paginators and one error type per refusal are written by hand in each language — because that is the half that decides whether a client is pleasant rather than merely correct.

  • TypeScript
  • Python
  • Rust
  • Kotlin
  • C#
  • C++

It tells you how much is left

Every answer carries X-RateLimit-Remaining and X-RateLimit-Reset, and a 429 carries Retry-After — including the 429 itself. Each library reads them and waits exactly as long as the server asked, rather than guessing.

Or it tells you, instead of you asking

Seven webhook events — an episode airing, the three ways a list row moves, a post, and the two ends of a follow. Signed, queued in a table rather than in memory, and retried for about a day, so an application that was down for ten minutes finds out what it missed.

Absent is not zero

A follower count that is missing means “not yours to know”; a 0 means “nobody”. Every library keeps the two apart, because collapsing them is the one thing this api's own rules say a client must not do.

10 scopes, each with something behind it

A scope is added on the day the thing it guards starts checking for it, not on the day somebody imagines the feature. A consent screen that lists something nothing honours teaches people that agreeing is a formality.