mg #

mg is a free and open source library for accessing the official MyAnimeList V2 API using Go. All endpoints are supported except forums as of now.

If you find any bugs, please write to me at vidhukant@vidhukant.com

License #

Licenced under GNU General Public Licence V3

GNU GPL License: link

Copyright (c) 2022-2023 Vidhu Kant Sharma

Documentation #

for v1.0.1+

Documentation might be incomplete or buggy. If you want to submit any changes, or need any help, please feel free to contact me at the given E-mail address.

Initializing the mg client #

There are 2 ways to authenticate with mg:

Check out Authentication for more details.

 1package main
 2
 3import (
 4    "vidhukant.com/mg"
 5)
 6
 7func main() {
 8    var client mg.Client
 9    
10    // either this
11    client.MainAuth = "Bearer <your token here>"
12
13    // or this
14    client.ClientAuth = "<your client id here>"
15    client.ClientAuthOnly = true
16
17    // ...
18}

Anime #

Searching for an anime #

MainAuth or ClientAuth

 1// ...
 2
 3var animes []mg.Anime
 4
 5params := mg.SearchParamsNew()
 6params.SearchString = "mushishi"
 7
 8// optional; these are the default values
 9params.Limit = 100 // max = 100
10params.Offset = 0
11params.NSFW = false
12params.Fields = []string{}
13
14err := client.SearchAnime(&animes, params)
15if err != nil {
16    fmt.Println(err.Error())
17}
18
19for _, anime := range animes {
20    fmt.Println(anime.Title)
21}
22
23// ...

Getting an anime’s information #

MainAuth or ClientAuth

searching can be a bit inaccurate, if you know the anime’s ID (obtain it with SearchAnime!) and want to get detailed information, use this.

 1// ...
 2
 3var anime mg.Anime
 4
 5err := client.GetAnimeById(&anime, /*anime id*/ 457, /*fields*/ []string{})
 6if err != nil {
 7    fmt.Println(err.Error())
 8}
 9
10// ...

Getting anime ranking list #

MainAuth or ClientAuth

Accepted ranking types:

Ranking Type Description
mg.RankingTypeAll Top Anime Series
mg.RankingTypeByPopularity Top Anime by Popularity
mg.RankingTypeFavorite Top Favorited Anime
mg.RankingTypeAiring Top Airing Anime
mg.RankingTypeUpcoming Top Upcoming Anime
mg.RankingTypeTV Top Anime TV Series
mg.RankingTypeOVA Top Anime OVA Series
mg.RankingTypeMovie Top Anime Movies
mg.RankingTypeSpecial Top Anime Specials

Getting seasonal animes #

MainAuth or ClientAuth

Accepted seasons:

Accepted sorts:

 1// ...
 2
 3var seasonals []mg.Anime
 4
 5params := mg.SeasonalParamsNew()
 6
 7params.Year = "2023" // required
 8params.Season = mg.SeasonFall // required
 9
10// optional; these are the default values
11params.Sort = mg.SeasonSortByAnimeScore
12params.Limit = 100 // max = 500
13params.Offset = 0
14params.NSFW = false
15params.Fields = []string{}
16
17err := client.GetSeasonalAnime(&seasonals, params)
18if err != nil {
19    fmt.Println(err.Error())
20}
21
22// ...

Getting suggested animes #

MainAuth only

 1// ...
 2
 3var suggestedAnimes []mg.Anime
 4
 5params := mg.SuggestedParamsNew()
 6
 7// optional; these are the default values
 8params.Limit = 100 // max = 100
 9params.Offset = 0
10params.NSFW = false
11params.Fields = []string{}
12
13err := client.GetSeasonalAnime(&suggestedAnimes, params)
14if err != nil {
15    fmt.Println(err.Error())
16}
17
18// ...

Get any user’s anime list #

MainAuth or ClientAuth

This returns a boolean telling us if there is a next page on this user’s list. Simply increment the offset by the limit and create a new request in order to get the next page.

In order to get the complete list, repeat the process until nextPageExists becomes false.

Accepted statuses:

Accepted list sorts:

 1// ...
 2
 3var animes []mg.Anime
 4
 5params := mg.ListParamsNew()
 6
 7// optional; these are the default values
 8params.Username = "@me" // can speciy any public account
 9params.Status = mg.ListStatusAll
10params.Sort = mg.SortByListScore
11params.Limit = 100 // max = 1000
12params.Offset = 0
13params.NSFW = false
14params.Fields = []string{}
15
16nextPageExists, err := client.GetAnimeList(&animes, params)
17if err != nil {
18    fmt.Println(err.Error())
19}
20
21// ...

Updating an anime #

MainAuth only

Updates an anime in the the currently logged in user’s anime list. Adds the anime if it’s not in the list.

Accepted statuses:

Score range: 0 - 10

Priority range: 0 - 2

Priority Description
0 Low
1 Medium
2 High

RewatchValue range: 0 - 5

RewatchValue Description
0 None
1 Very Low
2 Low
3 Medium
4 High
5 Very High
 1// ...
 2
 3var res AnimeUpdateResponse
 4
 5// can omit any field but keep at least 1!
 6params := map[string]interface{} {
 7    mg.Status: mg.ListStatusCompleted,
 8    mg.IsRewatching: false,
 9    mg.Score: 10,
10    mg.EpisodesWatched: 26,
11    mg.Priority: 2,
12    mg.TimesRewatched: 2,
13    mg.RewatchValue: 5,
14    mg.Tags: "tags",
15    mg.Comments: "comments",
16}
17
18err := client.UpdateAnime(&res, /*anime id*/ 457, params)
19if err != nil {
20    fmt.Println(err.Error())
21}
22
23// ...

Deleting an anime from list #

MainAuth only

Deletes the anime from currently logged in user’s anime list. Returns an error if anime is already not in the list.

1// ...
2
3err := client.DeleteAnime(/*anime id*/ 457)
4if err != nil {
5    fmt.Println(err.Error())
6}
7
8// ...

Manga #

Searching for a manga #

MainAuth or ClientAuth

also works for light novels, etc

 1// ...
 2
 3var mangas []mg.Manga
 4
 5carams := mg.SearchParamsNew()
 6carams.SearchString = "empty box and zeroth maria"
 7
 8// optional; these are the default values
 9params.Limit = 100
10params.Offset = 0
11params.NSFW = false
12params.Fields = []string{}
13
14err := client.SearchManga(&mangas, params)
15if err != nil {
16    fmt.Println(err.Error())
17}
18
19for _, manga range mangas {
20    fmt.Println(manga.Title)
21}
22
23// ...

Getting a manga’s information #

MainAuth or ClientAuth

searching can be a bit inaccurate, if you know the manga’s ID (obtain it with SearchManga!) and want to get detailed information, use this.

 1// ...
 2
 3var manga mg.Manga
 4
 5err := client.GetMangaById(&manga, /*manga id*/ 55215, /*fields*/ []string{})
 6if err != nil {
 7    fmt.Println(err.Error())
 8}
 9
10// ...

Getting manga ranking list #

MainAuth or ClientAuth

Accepted ranking types:

RankingType Description
mg.RankingTypeAll All
mg.RankingTypeByPopularity Most Popular
mg.RankingTypeFavorite Most Favorited
mg.RankingTypeManga Top Manga
mg.RankingTypeNovel Top Novels
mg.RankingTypeOneShot Top One-shots
mg.RankingTypeDoujin Top Doujinshi
mg.RankingTypeManhwa Top Manhwa
mg.RankingTypeManhua Top Manhua

RankedManga is just the Manga struct with an extra field RankNum int

 1// ...
 2
 3var rankingList []mg.RankedManga
 4
 5params := mg.RankingParamsNew()
 6
 7// optional; these are the default values
 8params.RankingType = mg.RankingTypeAll
 9params.Limit = 100 // max = 500
10params.Offset = 0
11params.NSFW = false
12params.Fields = []string{}
13
14err := client.GetAnimeRanking(&rankingList, params)
15if err != nil {
16    fmt.Println(err.Error())
17}
18
19// ...

Get any user’s manga list #

MainAuth or ClientAuth

This returns a boolean telling us if there is a next page on this user’s list. Simply increment the offset by the limit and create a new request in order to get the next page.

In order to get the complete list, repeat the process until nextPageExists becomes false.

Accepted statuses:

Accepted list sorts:

 1// ...
 2
 3var mangas []mg.Manga
 4
 5params := mg.ListParamsNew()
 6
 7// optional; these are the default values
 8params.Username = "@me" // can speciy any public account
 9params.Status = mg.ListStatusAll
10params.Sort = mg.SortByListScore
11params.Limit = 100 // max = 1000
12params.Offset = 0
13params.NSFW = false
14params.Fields = []string{}
15
16nextPageExists, err := client.GetMangaList(&mangas, params)
17if err != nil {
18    fmt.Println(err.Error())
19}
20
21// ...

Updating a manga #

MainAuth only

Updates a manga in the the currently logged in user’s manga list. Adds the manga if it’s not in the list.

Accepted statuses:

Score range: 0 - 10

Priority range: 0 - 2

Priority Description
0 Low
1 Medium
2 High

RereadValue range: 0 - 5

RereadValue Description
0 None
1 Very Low
2 Low
3 Medium
4 High
5 Very High
 1// ...
 2
 3var res MangaUpdateResponse
 4
 5// can omit any field but keep at least 1!
 6params := map[string]interface{} {
 7    mg.Status: mg.ListStatusReading,
 8    mg.IsRereading: false,
 9    mg.Score: 10,
10    mg.VolumesRead: 2,
11    mg.ChaptersRead: 20,
12    mg.Priority: 2,
13    mg.TimesReread: 0,
14    mg.RereadValue: 5,
15    mg.Tags: "tags",
16    mg.Comments: "comments",
17}
18
19err := client.UpdateManga(&res, /*manga id*/ 55215, params)
20if err != nil {
21    fmt.Println(err.Error())
22}
23
24// ...

Deleting a manga from list #

MainAuth only

Deletes the manga from currently logged in user’s manga list. Returns an error if manga is already not in the list.

1// ...
2
3err := client.DeleteManga(/*manga id*/ 55215)
4if err != nil {
5    fmt.Println(err.Error())
6}
7
8// ...

User #

Get logged in user’s details #

MainAuth only

MyAnimeList’s official API doesn’t support getting another user’s details. Only the currently logged in user.

 1// ...
 2
 3var user mg.User
 4
 5// to get user's list statistics or not
 6getStatistics := true
 7
 8err := client.GetSelfInfo(&user, getStatistics)
 9if err != nil {
10    fmt.Println(err.Error())
11}
12
13// ...

Authentication #

With maltoken #

You can use maltoken, which provides an easy way to automate this whole process. Here’s an example:

Go to https://myanimelist.net/apiconfig to generate your very own Client ID.

Then use this code to authenticate:

replace PORT with the port you set as the App Redirect URL while creating a Client ID

 1package main
 2
 3import (
 4    "fmt"
 5    mt "vidhukant.com/maltoken"
 6)
 7
 8func main() {
 9    // optionally change the HTML that is shown in the browser
10    mt.SuccessHTML = "<p>Yee haw</p>"
11    mt.BadRequestHTML = "<p>Y u do this</p>"
12
13    // The %s is optional
14    mt.ErrHTML = `
15        <style>
16            body {
17                color: red;
18            }
19        </style>
20        <p>An error occoured: %s</p>
21    `
22    
23    cid := "<your_client_id_here>"
24    challenge, link := mt.GetChallengeLink(cid)
25    
26    fmt.Println("Open this link in your browser: ", link)
27    
28    res, err := mt.Listen(cid, challenge, PORT)
29    if err != nil {
30        fmt.Println(err.Error())
31    }
32    
33    fmt.Println("API Response:", res)
34}

With maltoken-cli #

Install maltoken-cli:

1go install vidhukant.com/maltoken-cli@latest

Then run like this (flags are optional):

1maltoken-cli --launch --client-id "<your_client_id_here>" --port 8000

Run maltoken-cli -h for more info.

Manually/other options #

Otherwise, check out this guide to know more about how this whole process works.