Harshit
Building crunchy-api: A Modern TypeScript Client for Crunchyroll
Observation

Building crunchy-api: A Modern TypeScript Client for Crunchyroll

Apr 19, 2026
3 min read
Harshit

I love anime—Tokyo Ghoul, Attack on Titan, Fullmetal Alchemist: Brotherhood. As a developer, naturally, I wanted to pull my watch history and currently watching lists into my own apps. The problem? Crunchyroll doesn't have a public, documented API for developers to easily play with.

So, I built one.

Enter crunchy-api

I engineered crunchy-api, a fully-typed TypeScript client that seamlessly interacts with Crunchyroll's internal API. It handles token generation, session management, and parses enormous JSON payloads into strict, predictable TypeScript interfaces.

Here’s how you can use it to pull your own data.

1. The Core Client

The foundation of the package is the CrunchyrollClient. It's designed to run beautifully in Node.js, edge runtimes, or Next.js React Server Components.

Code
import { CrunchyrollClient } from "crunchy-api";
 
const client = new CrunchyrollClient({
  locale: "en-US",
  timeoutMs: 15000,
});
Tap to expand

2. Authentication Contexts

Crunchyroll has a few ways to authenticate. For public data (like fetching season episodes), you can just grab an anonymous token:

Code
// 1. Get an anonymous token for public catalog browsing
const anonToken = await client.getAnonymousToken();
console.log("Token:", anonToken.access_token);
Tap to expand

To access your personal Watch History or custom Crunchylists, you need to use your refresh token cookie (etp_rt).

Code
// 2. Authenticate using your browser cookie
const session = await client.authenticateWithRtCookie({
  cookie: process.env.CRUNCHYROLL_COOKIE,
  deviceType: "Chrome on Android"
});
Tap to expand

The client handles the heavy lifting of hitting the underlying OAuth endpoints, resolving profiles, and managing the active session automatically.

3. Pulling Your Anime Data

Once authenticated, fetching data is as simple as calling a typed method. Under the hood, I've mapped out complex objects (like CrunchyItem, EpisodeInfo, and WatchHistoryEntry) so you get perfect intellisense and avoid parsing errors.

Code
// Fetch what you are currently watching
const currentlyWatching = await client.getCurrentlyWatching({
  page_size: 10
});
 
// Or fetch your custom Crunchylists
const lists = await client.getCrunchylists();
 
// Get specific list items
if (lists.data.length > 0) {
  const items = await client.getCrunchylistItems(lists.data[0].id);
  console.log(items.data);
}
Tap to expand

Wait, before we even authenticate, you can totally fetch public catalog data globally.

Let's fetch season episodes of Wistoria S2 anonymously, directly pulling info using client.getSeasonEpisodes(seasonId) inside this Next.js server component:

Live Data / Season Episodes

Wistoria: Wand and Sword (S2)

Wistoria: Wand and Sword Special Episode

1. Wistoria: Wand and Sword Special Episode

EP

A look back at Season 1 and a short preview of what's to come!

Barrier Day

13. Barrier Day

EP

Two months after the all-student praxis, Will and the rest of the sixth-years face their written exams with their ascension to the tower on the line, but Edward's final question shocks everyone. Meanwhile in Urbus Rigarden, the new year's celebration, Terminalia, begins. Crowds watch as the Magia Vander lay out a new Great Barrier. Then, a disturbing magic circle appears in the night sky.

The Party from Hell Begins

14. The Party from Hell Begins

EP

A horde of monsters from the deepest floors appears, transforming the festive capital into a hellish scene. Sensing that they're the targets, the Magia Vander stay within the tower. The city’s defenses fall before the mage slayer wielding High Dinoboros. Colette and the dwarves join Will, putting up a desperate resistance. The Headless activates a magic circle and a Devander appears before Will!

One Single Magic Spell

15. One Single Magic Spell

EP

Rosty sacrifices himself to protect Will. The sight causes Will to lose himself, but Workner swoops in to save him. Edward and the other mages put their lives on the line, but the city’s defenses begin to collapse and enemies close in. As Will laments his lack of magic, Finn appears before him carrying a sword. Will possesses but a single spell. Finn asks Will its name and extends a sword to him.

And the Story Begins

16. And the Story Begins

EP

Guided by Finn, Will awakens the power sleeping within him. Enveloped in a silvery light, he slashes through the monsters in the capital, targeting the Devander. He faces the mage-slayer-wielding monster alone. The other students are moved as they watch Will, someone they scorned, do battle. With their cheers at his back, Will presses the fight, but the Devander’s attack breaks his sword in two.

The Day of Departure

17. The Day of Departure

EP

Through the combined powers of wand and sword, Will defeats the Devander with his mageblade Wis. Despite mass casualties and the leader of the attack going unknown, the capital begins to move toward recovery. And so, the day of the magical academy graduation arrives. After losing Rosty and having his path to the tower cut off, Will watches as the names of those ascending to the tower are read...

The First Bloom

18. The First Bloom

EP

At last, Will sets foot in the tower where Elfaria awaits. His excitement at seeing the town on the first floor is short-lived. The Bloom begins and Sion, Colette, Lihanna, and Wignall, who were all scouted as students, are welcomed into their respective factions. Will and the others learn the truth—without a faction, they will spend their lives as cogs in the machine supporting the tower.

He Said He Would No Longer Hang His Head

19. He Said He Would No Longer Hang His Head

EP

The First Bloom has begun. Will takes Julius's magic into his mageblade and defeats the monsters that were summoned to the ceremony grounds. Five factions, including the ice faction, want to recruit him, but Kreutz puts a stop to it. To test Will's aptitude as a mage, Kreutz orders him to battle a Wors Ooze, a monster that can only be defeated through attacks carrying magical energy.

Teachings of the Witch

20. Teachings of the Witch

EP

Wors Oozes can only be defeated with magic. With the Second Bloom in just one week's time, Will seeks Julius's help and concentrates his efforts on training to anchor magical energy in his sword. Meanwhile, Kreutz wants Will and Will's Wis power in his grasp, so he dispatches assassins. As high mages from the Upper Institute close in on Will and Julius, a mysterious witch appears to save them.

The Page to Be Turned

21. The Page to Be Turned

EP

Cerridwen guides Will through his memories to discover the origins of Wis, revealing a cherished emotion.

Hoping, Blooming, Thundering

22. Hoping, Blooming, Thundering

EP

Will and Julius try their hand at the Second Bloom. Under the intense gazes of the tower high mages and the Magia Vander, Will confronts a Wors Ooze once more. When he and Elfaria were young, they created magic together. Thinking back on the origin of his sword, Will calls forth Wis through his own power.

A Roar of Ice and Lightning

23. A Roar of Ice and Lightning

EP

The moment Elfaria moves to accept Will into the ice faction, Zeo swoops in and steals him away. Elfaria erupts with rage, and suddenly the hall becomes a battleground for the two Magia Vander. Will awakens, bewildered by the fight revolving around him. The epic battle fought between two paragons of the world of magic escalates as they hurl their most powerful of spells at one another.

A Story of a Dream with No End

24. A Story of a Dream with No End

EP

Elfaria and Zeo's fierce battle over Will remains undecided even after an exchange of their most powerful spells, leaving the ceremonial hall in ruins. Aaron, Masterias Noah, intervenes with a way to settle the matter. And so the Second Bloom comes to an end. When the celebratory party is in full swing, Workner shows Will outside to meet a certain female student.

You can also snag a dedicated episode instance, leveraging client.getEpisodeInfo(episodeId) just like this:

A Story of a Dream with No End
Wistoria: Wand and SwordS2 E24

A Story of a Dream with No End

Elfaria and Zeo's fierce battle over Will remains undecided even after an exchange of their most powerful spells, leaving the ceremonial hall in ruins. Aaron, Masterias Noah, intervenes with a way to settle the matter. And so the Second Bloom comes to an end. When the celebratory party is in full swing, Workner shows Will outside to meet a certain female student.

And here is another live demo connecting securely and dumping my personal Crunchylist dynamically inside this blog content!

Live Data / Crunchylists

Best Action

Currently has 1 saved shows.

Building a Next.js Integration Harness

To prove out the package, I built a Next.js integration harness that runs on the server and invokes each package method. This ensures that features like force-dynamic API routes and React Server Components play nicely with the crunchy-api fetch implementations.

Because the returned data can be nested (e.g., episode info is sometimes wrapped in a panel object, and image arrays are deeply nested), I built a robust normalizer component:

Code
function getImageUrl(images: any): string | null {
  const sources = images.thumbnail || images.poster_wide || images.poster_tall;
  if (Array.isArray(sources) && sources.length > 0 && Array.isArray(sources[0])) {
    const list = sources[0];
    const item = list.length > 3 ? list[3] : list[list.length - 1];
    return item?.source || null;
  }
  return null;
}
Tap to expand

If you're building an anime tracker, Discord bot, or a Next.js portfolio widget that brags about what you’re currently watching, crunchy-api handles all the session management and TypeScript generics for you out of the box.

Here is a live demo pulling my actual watch history right now, rendered natively in MDX using Server Components:

Grab the package from NPM or check out the source on my GitHub. Now, back to watching AOT.