A Go client for the Jikan API that retries on 429s, caches responses, and actually validates IDs before burning a network request. Saves you from writing the same HTTP wrapper again.
go get github.com/Sethispr/jikanGopackage main
import (
"context"
"fmt"
"log"
"time"
"github.com/Sethispr/jikanGo"
)
func main() {
client := jikan.New()
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
char, err := client.Character.ByID(ctx, 1) // Spike Spiegel's ID is 1
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s (%s)\n", char.Name, char.NameKanji)
fmt.Printf("Known as: %v\n", char.Nicknames)
}- Complete API: Characters, anime, manga, clubs, genres, people, seasons, producers, etc
- Strongly Typed: Access
char.NameKanji,char.Nicknames, etc directly, no type assertions - Retries: Handles 429s and other errors with exponential backoff
- Caching: Optional response caching with TTL
- Rate Limiting: Optional built in request throttling
- Pagination: Returns
*PaginationwithLastPageandHasNext - Context Support: All methods accept
context.Contextfor timeouts
Jikan allows 3 requests per second. Turn on the built in limiter to stay under this limit automatically:
// Default: 3 requests per second (Jikan's limit)
client := jikan.New(jikan.WithRateLimit(3))Customize the rate:
// 5 requests per second (only if you have special access)
client := jikan.New(jikan.WithRateLimit(5))Advanced configuration (custom burst size):
import "golang.org/x/time/rate"
// Allow bursts of 5, then limit to 3 per second
lim := rate.NewLimiter(rate.Every(333*time.Millisecond), 5)
client := jikan.New(jikan.WithRateLimiter(lim))The rate limiter respects context cancellation. If your context times out while waiting for the rate limiter, it returns immediately with the context error.
Avoid hitting the API twice for the same data. The client accepts any cache implementing the Cache interface.
In memory cache (included):
cache := jikan.NewMemoryCache()
defer cache.Stop()
client := jikan.New(
jikan.WithCache(cache, 5*time.Minute),
)Bring your own (Redis, etc):
type RedisCache struct { client *redis.Client }
func (r *RedisCache) Get(ctx context.Context, key string, dst interface{}) error { ... }
func (r *RedisCache) Set(ctx context.Context, key string, val interface{}, ttl time.Duration) error { ... }
func (r *RedisCache) Delete(ctx context.Context, key string) error { ... }
client := jikan.New(jikan.WithCache(&RedisCache{redisClient}, time.Hour))Skip cache for specific requests:
ctx := jikan.NoCache(context.Background())
char, _ := client.Character.ByID(ctx, 1) // Forces API callGet combined data (one call instead of four):
full, err := client.Character.Full(ctx, 1)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Appears in %d anime and %d manga\n", len(full.Anime), len(full.Manga))Search with pagination:
results, pg, err := client.Character.Search(ctx, "Spike", 1)
if err != nil {
log.Fatal(err)
}
if pg.HasNext {
fmt.Println("More pages available")
}Filter genres safely:
themes, _, err := client.Genre.Anime(ctx, jikan.GenreThemes, 1, 25)
if err != nil {
log.Fatal(err)
}
for _, t := range themes {
fmt.Println(t.Name)
}See examples/ folder for working CLIs.