Important
- There was previously a hosted version of this API for showcasing purposes only, and it was misused; since then, there have been no other hosted versions. It is recommended to deploy your own instance for personal use by customizing the API as you need it to be.
- This API is just an unofficial API for hianimez.to and is in no other way officially related to the same.
- The content that this API provides is not mine, nor is it hosted by me. These belong to their respective owners. This API just demonstrates how to build an API that scrapes websites and uses their content.
- Installation
- Configuration
- Host your instance
- Documentation
- GET Anime Home Page
- GET Anime A-Z List
- GET Anime Qtip Info
- GET Anime About Info
- GET Search Results
- GET Search Suggestions
- GET Producer Animes
- GET Genre Animes
- GET Category Animes
- GET Estimated Schedules
- GET Anime Episodes
- GET Anime Next Episode Schedule
- GET Anime Episode Servers
- GET Anime Episode Streaming Links
- Development
- Contributors
- Thanks
- Support
- License
- Star History
-
Clone the repository and move into the directory.
git clone https://github.com/ghoshRitesh12/aniwatch-api.git cd aniwatch-api
-
Install all the dependencies.
npm i #or yarn install or pnpm i
-
Start the server!
npm start #or yarn start or pnpm start
Now the server should be running on http://localhost:4000
The Docker image is available at The GitHub Container Registry.
Run the following commands to pull and run the docker image.
docker run -d --name aniwatch-api -p 4000:4000 ghcr.io/ghoshritesh12/aniwatch
The above command will start the server on port 4000. You can access the server at http://localhost:4000, and you can also change the port by changing the -p
option to -p <port>:4000
.
The -d
flag runs the container in detached mode, and the --name
flag is used to name the container that's about to run.
Currently this API supports parsing of only one custom header, and more may be implemented in the future to accommodate varying needs.
Aniwatch-Cache-Expiry
: This custom request header is used to specify the cache expiration duration in seconds (defaults to 300 or 5 mins if the header is missing). TheANIWATCH_API_REDIS_CONN_URL
env is required for this custom header to function as intended; otherwise, there's no point in setting this custom request header. A response header of the same name is also returned with the value set to the cache expiration duration in seconds ifANIWATCH_API_REDIS_CONN_URL
env is set.
More info can be found in the .env.example
file, where envs' having a value that is contained within <
>
angled brackets, commented out or not, are just examples and should be replaced with relevant ones.
ANIWATCH_API_PORT
: Port number of the aniwatch API.ANIWATCH_API_WINDOW_MS
: Duration to track requests for rate limiting (in milliseconds).ANIWATCH_API_MAX_REQS
: Maximum number of requests in theANIWATCH_API_WINDOW_MS
time period.ANIWATCH_API_CORS_ALLOWED_ORIGINS
: Allowed origins, separated by commas and no spaces in between (CSV).ANIWATCH_API_DEPLOYMENT_ENV
: The deployment environment of the Aniwatch API. Many configurations depend on this env, rate limiting being one of them. It must be set incase of serverless deployments; otherwise, you may run into weird issues. Possible values:'nodejs' | 'docker' | 'vercel' | 'render' | 'cloudflare-workers'
.ANIWATCH_API_HOSTNAME
: Set this to your api instance's hostname to enable rate limiting, don't have this value if you don't wish to rate limit.ANIWATCH_API_REDIS_CONN_URL
: This env is optional by default and can be set to utilize Redis caching functionality. It has to be a valid connection URL; otherwise, the Redis client can throw unexpected errors.ANIWATCH_API_S_MAXAGE
: Specifies the maximum amount of time (in seconds) a resource is considered fresh when served by a CDN cache.ANIWATCH_API_STALE_WHILE_REVALIDATE
: Specifies the amount of time (in seconds) a resource is served stale while a new one is fetched.
Caution
For personal deployments:
- If you want to have rate limiting in your application, then set the
ANIWATCH_API_HOSTNAME
env to your deployed instance's hostname; otherwise, don't set or have this env at all. If you set this env to an incorrect value, you may face other issues. - It's optional by default, but if you want to have endpoint response caching functionality, then set the
ANIWATCH_API_REDIS_CONN_URL
env to a valid Redis connection URL. If the connection URL is invalid, the Redis client can throw unexpected errors. - You may or may not wanna remove the last
if
block within the IIFE in theserver.ts
file. It is for render free deployments only, as their free tier has an approx 10 or 15 minute sleep time. Thatif
block keeps the server awake and prevents it from sleeping. You can enable the automatic health check by setting the environment variablesANIWATCH_API_HOSTNAME
to your deployment's hostname, andANIWATCH_API_DEPLOYMENT_ENV
torender
in your environment variables. If you are not using render, you can remove thatif
block. - If you are using a serverless deployment, then set the
ANIWATCH_API_DEPLOYMENT_ENV
env tovercel
orrender
orcloudflare-workers
depending on your deployment platform. This is because the API uses this env to configure different functionalities, such as rate limiting, graceful shutdown or hosting static files.
Deploy your own instance of Aniwatch API on Vercel.
Note
When deploying to vercel, you must set the env named ANIWATCH_API_DEPLOYMENT_ENV
to vercel
for proper functioning of the API.
Deploy your own instance of Aniwatch API on Render.
Note
When deploying to render, you must set the env named ANIWATCH_API_DEPLOYMENT_ENV
to render
for proper functioning of the API.
The endpoints exposed by the api are listed below with examples that uses the Fetch API, but you can use any http library.
/api/v2/hianime/home
const resp = await fetch("/api/v2/hianime/home");
const data = await resp.json();
console.log(data);
{
success: true,
data: {
genres: ["Action", "Cars", "Adventure", ...],
latestEpisodeAnimes: [
{
id: string,
name: string,
poster: string,
type: string,
episodes: {
sub: number,
dub: number,
}
},
{...},
],
spotlightAnimes: [
{
id: string,
name: string,
jname: string,
poster: string,
description: string,
rank: number,
otherInfo: string[],
episodes: {
sub: number,
dub: number,
},
},
{...},
],
top10Animes: {
today: [
{
episodes: {
sub: number,
dub: number,
},
id: string,
name: string,
poster: string,
rank: number
},
{...},
],
month: [...],
week: [...]
},
topAiringAnimes: [
{
id: string,
name: string,
jname: string,
poster: string,
},
{...},
],
topUpcomingAnimes: [
{
id: string,
name: string,
poster: string,
duration: string,
type: string,
rating: string,
episodes: {
sub: number,
dub: number,
}
},
{...},
],
trendingAnimes: [
{
id: string,
name: string,
poster: string,
rank: number,
},
{...},
],
mostPopularAnimes: [
{
id: string,
name: string,
poster: string,
type: string,
episodes: {
sub: number,
dub: number,
}
},
{...},
],
mostFavoriteAnimes: [
{
id: string,
name: string,
poster: string,
type: string,
episodes: {
sub: number,
dub: number,
}
},
{...},
],
latestCompletedAnimes: [
{
id: string,
name: string,
poster: string,
type: string,
episodes: {
sub: number,
dub: number,
}
},
{...},
],
}
}
/api/v2/hianime/azlist/{sortOption}?page={page}
Parameter | Type | Description | Required? | Default |
---|---|---|---|---|
sortOption |
string | The az-list sort option. Possible values include: "all", "other", "0-9" and all english alphabets . | Yes | -- |
Parameter | Type | Description | Required? | Default |
---|---|---|---|---|
page |
number | The page number of the result. | No | 1 |
const resp = await fetch("/api/v2/hianime/azlist/0-9?page=1");
const data = await resp.json();
console.log(data);
{
success: true,
data: {
sortOption: "0-9",
animes: [
{
id: string,
name: string,
jname: string,
poster: string,
duration: string,
type: string,
rating: string,
episodes: {
sub: number ,
dub: number
}
},
{...}
],
totalPages: 1,
currentPage: 1,
hasNextPage: false
}
}
/api/v2/hianime/qtip/{animeId}
Parameter | Type | Description | Required? | Default |
---|---|---|---|---|
animeId |
string | The unique anime id (in kebab case). | Yes | -- |
const resp = await fetch("/api/v2/hianime/qtip/one-piece-100");
const data = await resp.json();
console.log(data);
{
success: true,
data: {
anime: {
id: "one-piece-100",
name: "One Piece",
malscore: string,
quality: string,
episodes: {
sub: number,
dub: number
},
type: string,
description: string,
jname: string,
synonyms: string,
aired: string,
status: string,
genres: ["Action", "Adventure", "Comedy", "Drama", "Fantasy", "Shounen", "Drama", "Fantasy", "Shounen", "Fantasy", "Shounen", "Shounen", "Super Power"]
}
}
}
/api/v2/hianime/anime/{animeId}
Parameter | Type | Description | Required? | Default |
---|---|---|---|---|
animeId |
string | The unique anime id (in kebab case). | Yes | -- |
const resp = await fetch("/api/v2/hianime/anime/attack-on-titan-112");
const data = await resp.json();
console.log(data);
{
success: true,
data: {
anime: [
info: {
id: string,
name: string,
poster: string,
description: string,
stats: {
rating: string,
quality: string,
episodes: {
sub: number,
dub: number
},
type: string,
duration: string
},
promotionalVideos: [
{
title: string | undefined,
source: string | undefined,
thumbnail: string | undefined
},
{...},
],
characterVoiceActor: [
{
character: {
id: string,
poster: string,
name: string,
cast: string
},
voiceActor: {
id: string,
poster: string,
name: string,
cast: string
}
},
{...},
]
}
moreInfo: {
aired: string,
genres: ["Action", "Mystery", ...],
status: string,
studios: string,
duration: string
...
}
],
mostPopularAnimes: [
{
episodes: {
sub: number,
dub: number,
},
id: string,
jname: string,
name: string,
poster: string,
type: string
},
{...},
],
recommendedAnimes: [
{
id: string,
name: string,
poster: string,
duration: string,
type: string,
rating: string,
episodes: {
sub: number,
dub: number,
}
},
{...},
],
relatedAnimes: [
{
id: string,
name: string,
poster: string,
duration: string,
type: string,
rating: string,
episodes: {
sub: number,
dub: number,
}
},
{...},
],
seasons: [
{
id: string,
name: string,
title: string,
poster: string,
isCurrent: boolean
},
{...}
]
}
}
# basic example
/api/v2/hianime/search?q={query}&page={page}
# advanced example
/api/v2/hianime/search?q={query}&page={page}&genres={genres}&type={type}&sort={sort}&season={season}&language={sub_or_dub}&status={status}&rated={rating}&start_date={yyyy-mm-dd}&end_date={yyyy-mm-dd}&score={score}
Parameter | Type | Description | Required? | Default |
---|---|---|---|---|
q |
string | The search query, i.e. the title of the item you are looking for. | Yes | -- |
page |
number | The page number of the result. | No | 1 |
type |
string | Type of the anime. eg: movie |
No | -- |
status |
string | Status of the anime. eg: finished-airing |
No | -- |
rated |
string | Rating of the anime. eg: r+ or pg-13 |
No | -- |
score |
string | Score of the anime. eg: good or very-good |
No | -- |
season |
string | Season of the aired anime. eg: spring |
No | -- |
language |
string | Language category of the anime. eg: sub or sub-&-dub |
No | -- |
start_date |
string | Start date of the anime(yyyy-mm-dd). eg: 2014-10-2 |
No | -- |
end_date |
string | End date of the anime(yyyy-mm-dd). eg: 2010-12-4 |
No | -- |
sort |
string | Order of sorting the anime result. eg: recently-added |
No | -- |
genres |
string | Genre of the anime, separated by commas. eg: isekai,shounen |
No | -- |
[!TIP] For both
start_date
andend_date
, year must be mentioned. If you wanna omit date or month specify0
instead. Eg: omitting date -> 2014-10-0, omitting month -> 2014-0-12, omitting both -> 2014-0-0
// basic example
const resp = await fetch("/api/v2/hianime/search?q=titan&page=1");
const data = await resp.json();
console.log(data);
// advanced example
const resp = await fetch(
"/api/v2/hianime/search?q=girls&genres=action,adventure&type=movie&sort=score&season=spring&language=dub&status=finished-airing&rated=pg-13&start_date=2014-0-0&score=good"
);
const data = await resp.json();
console.log(data);
{
success: true,
data: {
animes: [
{
id: string,
name: string,
poster: string,
duration: string,
type: string,
rating: string,
episodes: {
sub: number,
dub: number,
}
},
{...},
],
mostPopularAnimes: [
{
episodes: {
sub: number,
dub: number,
},
id: string,
jname: string,
name: string,
poster: string,
type: string
},
{...},
],
currentPage: 1,
totalPages: 1,
hasNextPage: false,
searchQuery: string,
searchFilters: {
[filter_name]: [filter_value]
...
}
}
}
/api/v2/hianime/search/suggestion?q={query}
Parameter | Type | Description | Required? | Default |
---|---|---|---|---|
q |
string | The search suggestion query. | Yes | -- |
const resp = await fetch("/api/v2/hianime/search/suggestion?q=monster");
const data = await resp.json();
console.log(data);
{
success: true,
data: {
suggestions: [
{
id: string,
name: string,
poster: string,
jname: string,
moreInfo: ["Jan 21, 2022", "Movie", "17m"]
},
{...},
]
}
}
/api/v2/hianime/producer/{name}?page={page}
Parameter | Type | Description | Required? | Default |
---|---|---|---|---|
name |
string | The name of anime producer (in kebab case). | Yes | -- |
Parameter | Type | Description | Required? | Default |
---|---|---|---|---|
page |
number | The page number of the result. | No | 1 |
const resp = await fetch("/api/v2/hianime/producer/toei-animation?page=2");
const data = await resp.json();
console.log(data);
{
success: true,
data: {
producerName: "Toei Animation Anime",
animes: [
{
id: string,
name: string,
poster: string,
duration: string,
type: string,
rating: string,
episodes: {
sub: number,
dub: number,
}
},
{...},
],
top10Animes: {
today: [
{
episodes: {
sub: number,
dub: number,
},
id: string,
name: string,
poster: string,
rank: number
},
{...},
],
month: [...],
week: [...]
},
topAiringAnimes: [
{
episodes: {
sub: number,
dub: number,
},
id: string,
jname: string,
name: string,
poster: string,
type: string
},
{...},
],
currentPage: 2,
totalPages: 11,
hasNextPage: true
}
}
/api/v2/hianime/genre/{name}?page={page}
Parameter | Type | Description | Required? | Default |
---|---|---|---|---|
name |
string | The name of anime genre (in kebab case). | Yes | -- |
Parameter | Type | Description | Required? | Default |
---|---|---|---|---|
page |
number | The page number of the result. | No | 1 |
const resp = await fetch("/api/v2/hianime/genre/shounen?page=2");
const data = await resp.json();
console.log(data);
{
success: true,
data: {
genreName: "Shounen Anime",
animes: [
{
id: string,
name: string,
poster: string,
duration: string,
type: string,
rating: string,
episodes: {
sub: number,
dub: number,
}
},
{...},
],
genres: ["Action", "Cars", "Adventure", ...],
topAiringAnimes: [
{
episodes: {
sub: number,
dub: number,
},
id: string,
jname: string,
name: string,
poster: string,
type: string
},
{...},
],
currentPage: 2,
totalPages: 38,
hasNextPage: true
}
}
/api/v2/hianime/category/{name}?page={page}
Parameter | Type | Description | Required? | Default |
---|---|---|---|---|
category |
string | The category of anime. | Yes | -- |
Parameter | Type | Description | Required? | Default |
---|---|---|---|---|
page |
number | The page number of the result. | No | 1 |
// categories -> "most-favorite", "most-popular", "subbed-anime", "dubbed-anime", "recently-updated", "recently-added", "top-upcoming", "top-airing", "movie", "special", "ova", "ona", "tv", "completed"
const resp = await fetch("/api/v2/hianime/category/tv?page=2");
const data = await resp.json();
console.log(data);
{
success: true,
data: {
category: "TV Series Anime",
animes: [
{
id: string,
name: string,
poster: string,
duration: string,
type: string,
rating: string,
episodes: {
sub: number,
dub: number,
}
},
{...},
],
genres: ["Action", "Cars", "Adventure", ...],
top10Animes: {
today: [
{
episodes: {
sub: number,
dub: number,
},
id: string,
name: string,
poster: string,
rank: number
},
{...},
],
month: [...],
week: [...]
},
currentPage: 2,
totalPages: 100,
hasNextPage: true
}
}
/api/v2/hianime/schedule?date={date}
Parameter | Type | Description | Required? | Default |
---|---|---|---|---|
date |
string | The date of the desired schedule in the following format: (yyyy-mm-dd). | Yes | -- |
const resp = await fetch("/api/v2/hianime/schedule?date=2024-06-09");
const data = await resp.json();
console.log(data);
{
success: true,
data: {
scheduledAnimes: [
{
id: string,
time: string, // 24 hours format
name: string,
jname: string,
airingTimestamp: number,
secondsUntilAiring: number
},
{...}
]
}
}
/api/v2/hianime/anime/{animeId}/episodes
Parameter | Type | Description | Required? | Default |
---|---|---|---|---|
animeId |
string | The unique anime id. | Yes | -- |
const resp = await fetch("/api/v2/hianime/anime/steinsgate-3/episodes");
const data = await resp.json();
console.log(data);
{
success: true,
data: {
totalEpisodes: 24,
episodes: [
{
number: 1,
title: "Turning Point",
episodeId: "steinsgate-3?ep=213"
isFiller: false,
},
{...}
]
}
}
/api/v2/hianime/anime/{animeId}/next-episode-schedule
Parameter | Type | Description | Required? | Default |
---|---|---|---|---|
animeId |
string | The unique anime id. | Yes | -- |
const resp = await fetch(
"/api/v2/hianime/anime/steinsgate-3/next-episode-schedule"
);
const data = await resp.json();
console.log(data);
{
success: true,
data: {
airingISOTimestamp: string | null,
airingTimestamp: number | null,
secondsUntilAiring: number | null
}
}
/api/v2/hianime/episode/servers?animeEpisodeId={id}
Parameter | Type | Description | Required? | Default |
---|---|---|---|---|
animeEpisodeId |
string | The unique anime episode id. | Yes | -- |
const resp = await fetch(
"/api/v2/hianime/episode/servers?animeEpisodeId=steinsgate-0-92?ep=2055"
);
const data = await resp.json();
console.log(data);
{
success: true,
data: {
episodeId: "steinsgate-0-92?ep=2055",
episodeNo: 5,
sub: [
{
serverId: 4,
serverName: "vidstreaming",
},
{...}
],
dub: [
{
serverId: 1,
serverName: "megacloud",
},
{...}
],
raw: [
{
serverId: 1,
serverName: "megacloud",
},
{...}
]
}
}
/api/v2/hianime/episode/sources?animeEpisodeId={id}?server={server}&category={dub || sub || raw}
Parameter | Type | Description | Required? | Default |
---|---|---|---|---|
animeEpisodeId |
string | The unique anime episode id. | Yes | -- |
server |
string | The name of the server. | No | "hd-1" |
category |
string | The category of the episode ('sub', 'dub' or 'raw'). | No | "sub" |
const resp = await fetch(
"/api/v2/hianime/episode/sources?animeEpisodeId=steinsgate-3?ep=230&server=hd-1&category=dub"
);
const data = await resp.json();
console.log(data);
{
success: true,
data: {
headers: {
Referer: string,
"User-Agent": string,
...
},
sources: [
{
url: string, // .m3u8 hls streaming file
isM3U8: boolean,
quality?: string,
},
{...}
],
subtitles: [
{
lang: "English",
url: string, // .vtt subtitle file
},
{...}
],
anilistID: number | null,
malID: number | null
}
}
Pull requests and stars are always welcome. If you encounter any bug or want to add a new feature to this api, consider creating a new issue. If you wish to contribute to this project, read the CONTRIBUTING.md file.
Thanks to the following people for keeping this project alive and relevant.
Don't forget to leave a star π. You can also follow me on X (Twitter) @riteshgsh.
This project is licensed under the MIT License - see the LICENSE file for more details.