Note: This project is under development. Feedback, suggestions, and issues are highly appreciated!
Program to synchronize your AniList and MyAnimeList accounts.
- Bidirectional sync between AniList and MyAnimeList (anime and manga)
- OAuth2 authentication
- CLI interface
- Manual ID mappings and ignore rules via
mappings.yaml - Duplicate target detection with automatic conflict resolution
- Unmapped entries tracking with interactive management (
unmappedcommand) - Offline ID mapping using anime-offline-database (prevents incorrect season matches)
- Optional ARM API integration for online ID lookups
AniList:
- Go to AniList Developer Settings
- Create New Client with redirect URL:
http://localhost:18080/callback - Save Client ID and Client Secret
MyAnimeList:
- Go to MAL API Settings
- Create Application with redirect URL:
http://localhost:18080/callback - Save Client ID and Client Secret
Download docker-compose.example.yaml and edit credentials:
services:
sync:
image: ghcr.io/bigspawn/anilist-mal-sync:latest
ports:
- "18080:18080"
environment:
# User/Group ID for volume permissions (run: id -u / id -g)
- PUID=1000
- PGID=1000
# Required: API credentials
- ANILIST_CLIENT_ID=your_anilist_client_id
- ANILIST_CLIENT_SECRET=your_anilist_secret
- ANILIST_USERNAME=your_anilist_username
- MAL_CLIENT_ID=your_mal_client_id
- MAL_CLIENT_SECRET=your_mal_secret
- MAL_USERNAME=your_mal_username
# Optional: Watch mode interval (e.g., 12h, 1h, 30m)
# - WATCH_INTERVAL=12h
# Optional: Manual mappings file path
# - MAPPINGS_FILE_PATH=/home/appuser/.config/anilist-mal-sync/mappings.yaml
# Optional: ID Mapping settings
# - OFFLINE_DATABASE_ENABLED=true # Enable offline DB (default: true)
# - HATO_API_ENABLED=true # Enable Hato API (default: true)
# - HATO_API_URL=https://hato.malupdaterosx.moe # Hato API base URL
# - HATO_API_CACHE_DIR=/home/appuser/.config/anilist-mal-sync/hato-cache # Cache directory
# - HATO_API_CACHE_MAX_AGE=720h # Cache max age (default: 720h / 30 days)
# - ARM_API_ENABLED=false # Enable ARM API (default: false)
# - ARM_API_URL=https://arm.haglund.dev # ARM API base URL
# - JIKAN_API_ENABLED=false # Enable Jikan API for manga ID mapping
# - JIKAN_API_CACHE_DIR=/home/appuser/.config/anilist-mal-sync/jikan-cache
# - JIKAN_API_CACHE_MAX_AGE=168h
volumes:
- tokens:/home/appuser/.config/anilist-mal-sync
restart: unless-stopped
volumes:
tokens:docker-compose run --rm --service-ports sync login allFollow the URLs printed in terminal.
First, run dry-run mode to preview what will be synced without making actual changes.
docker-compose run --rm --service-ports sync sync --dry-runAfter a successful dry-run, you can start the service with real sync:
docker-compose up -dDone!
| Command | Description |
|---|---|
login |
Authenticate with services |
logout |
Remove authentication tokens |
status |
Check authentication status |
sync |
Synchronize anime/manga lists |
watch |
Run sync on interval |
unmapped |
Show and manage unmapped entries from last sync |
Login/Logout options:
| Short | Long | Description |
|---|---|---|
-s |
--service |
Service: anilist, myanimelist, all (default) |
Sync options:
| Short | Long | Description |
|---|---|---|
-c |
--config |
Path to config file (optional, uses env vars if not specified) |
-f |
--force |
Force sync all entries |
-d |
--dry-run |
Dry run without making changes |
--manga |
Sync manga instead of anime | |
--all |
Sync both anime and manga | |
--verbose |
Enable verbose logging | |
--reverse-direction |
Sync from MyAnimeList to AniList | |
--offline-db |
Enable offline database for anime ID mapping (default: true, ignored for --manga) |
|
--offline-db-force-refresh |
Force re-download offline database | |
--arm-api |
Enable ARM API for anime ID mapping (default: false, ignored for --manga) |
|
--arm-api-url |
ARM API base URL | |
--jikan-api |
Enable Jikan API for manga ID mapping (default: false, ignored for anime) |
Watch options:
| Short | Long | Description |
|---|---|---|
-i |
--interval |
Sync interval: 1h-168h (overrides config) |
--once |
Sync immediately then start watching |
Interval can be set via --interval flag or in config.yaml under watch.interval.
Unmapped options:
| Short | Long | Description |
|---|---|---|
--fix |
Interactively fix unmapped entries (ignore or map to MAL ID) | |
--ignore-all |
Add all unmapped entries to ignore list |
For backward compatibility, running anilist-mal-sync [options] without a command will execute sync.
Full config.yaml example:
oauth:
port: "18080"
redirect_uri: "http://localhost:18080/callback"
anilist:
client_id: "your_client_id"
client_secret: "your_secret"
auth_url: "https://anilist.co/api/v2/oauth/authorize"
token_url: "https://anilist.co/api/v2/oauth/token"
username: "your_username"
myanimelist:
client_id: "your_client_id"
client_secret: "your_secret"
auth_url: "https://myanimelist.net/v1/oauth2/authorize"
token_url: "https://myanimelist.net/v1/oauth2/token"
username: "your_username"
token_file_path: "" # Leave empty for default: ~/.config/anilist-mal-sync/token.json
mappings_file_path: "" # Leave empty for default: ~/.config/anilist-mal-sync/mappings.yaml
watch:
interval: "24h" # Sync interval for watch mode (1h-168h), can be overridden with --interval flag
http_timeout: "30s" # HTTP client timeout for API requests (default: 30s)
offline_database:
enabled: true
cache_dir: "" # Default: ~/.config/anilist-mal-sync/aod-cache
auto_update: true
arm_api:
enabled: false
base_url: "https://arm.haglund.dev" # Default: https://arm.haglund.dev
hato_api:
enabled: true # Enable Hato API for ID mapping (default: true)
base_url: "https://hato.malupdaterosx.moe" # Hato API base URL
cache_dir: "" # Leave empty for default: ~/.config/anilist-mal-sync/hato-cache
cache_max_age: "720h" # Cache max age (default: 720h / 30 days)
jikan_api:
enabled: false # Enable Jikan API for manga ID mapping (default: false)
cache_dir: "" # Default: ~/.config/anilist-mal-sync/jikan-cache
cache_max_age: "168h" # Cache max age (default: 168h / 7 days)The tool uses different ID mapping strategies for anime and manga:
When syncing anime (default or --all mode), the following strategies are used in order:
- Manual Mapping - User-defined AniList↔MAL mappings from
mappings.yaml - Direct ID lookup - If the entry already exists in your target list
- Offline Database (optional, enabled by default) - Local database from anime-offline-database
- Hato API (optional, enabled by default) - Online API for anime/manga ID mapping
- ARM API (optional, disabled by default) - Online fallback to arm-server
- Title matching - Match by title similarity
- API search - Search the target service API
When syncing manga (--manga mode), the following strategies are used:
- Manual Mapping - User-defined AniList↔MAL mappings from
mappings.yaml - Direct ID lookup - If the entry already exists in your target list
- Hato API (optional, enabled by default) - Online API for manga ID mapping
- Title matching - Match by title similarity
- Jikan API (optional, disabled by default) - Online API for manga ID mapping via Jikan (unofficial MAL API)
- API search - Search the target service API
Note:
- The offline database and ARM API are anime-only and automatically disabled when using
--mangaflag (without--all) to improve startup performance. - Hato API supports both anime and manga and is enabled by default.
- In reverse sync mode (
--reverse-direction), an additional MAL ID lookup strategy is used before API search to find entries by MAL ID on AniList.
You can define manual ID mappings and ignore rules in a YAML file (mappings.yaml):
manual_mappings:
- anilist_id: 12345
mal_id: 67890
comment: "Season 2 mapped manually"
ignore:
anilist_ids:
- 99999 # Title Name : reason for ignoring
titles:
- "Some Title to Ignore"Default location: ~/.config/anilist-mal-sync/mappings.yaml
You can also manage ignore rules interactively:
# Show unmapped entries from last sync
anilist-mal-sync unmapped
# Interactively fix unmapped entries (ignore or map to MAL ID)
anilist-mal-sync unmapped --fix
# Add all unmapped entries to ignore list
anilist-mal-sync unmapped --ignore-allConfiguration can be provided entirely via environment variables (recommended for Docker):
Required:
ANILIST_CLIENT_ID- AniList Client IDANILIST_CLIENT_SECRET- AniList Client Secret (also acceptsCLIENT_SECRET_ANILIST)ANILIST_USERNAME- AniList usernameMAL_CLIENT_ID- MyAnimeList Client IDMAL_CLIENT_SECRET- MyAnimeList Client Secret (also acceptsCLIENT_SECRET_MYANIMELIST)MAL_USERNAME- MyAnimeList username
Optional:
WATCH_INTERVAL- Sync interval for watch mode (e.g.,12h,24h)HTTP_TIMEOUT- HTTP client timeout for API requests (default:30s, e.g.,10s,1m)OAUTH_PORT- OAuth server port (default:18080)OAUTH_REDIRECT_URI- OAuth redirect URI (default:http://localhost:18080/callback)TOKEN_FILE_PATH- Token file path (default:~/.config/anilist-mal-sync/token.json)MAPPINGS_FILE_PATH- Path to manual mappings YAML file (default:~/.config/anilist-mal-sync/mappings.yaml)PUID/PGID- User/Group ID for Docker volume permissionsOFFLINE_DATABASE_ENABLED- Enable offline database for anime ID mapping (default:true, not used for manga-only sync)OFFLINE_DATABASE_CACHE_DIR- Cache directory (default:~/.config/anilist-mal-sync/aod-cache)OFFLINE_DATABASE_AUTO_UPDATE- Auto-update database (default:true)HATO_API_ENABLED- Enable Hato API for ID mapping (default:true, supports both anime and manga)HATO_API_URL- Hato API base URL (default:https://hato.malupdaterosx.moe)HATO_API_CACHE_DIR- Hato API cache directory (default:~/.config/anilist-mal-sync/hato-cache)HATO_API_CACHE_MAX_AGE- Hato API cache max age (default:720h/ 30 days)ARM_API_ENABLED- Enable ARM API for anime ID mapping (default:false, not used for manga-only sync)ARM_API_URL- ARM API base URL (default:https://arm.haglund.dev)JIKAN_API_ENABLED- Enable Jikan API for manga ID mapping (default:false, not used for anime sync)JIKAN_API_CACHE_DIR- Jikan API cache directory (default:~/.config/anilist-mal-sync/jikan-cache)JIKAN_API_CACHE_MAX_AGE- Jikan API cache max age (default:168h/ 7 days)
go install github.com/bigspawn/anilist-mal-sync@latest
anilist-mal-sync login all
anilist-mal-sync syncSee Quick Start for the recommended setup.
Using config file instead of environment variables:
docker run --rm -p 18080:18080 \
-e PUID=$(id -u) -e PGID=$(id -g) \
-v $(pwd)/config.yaml:/etc/anilist-mal-sync/config.yaml:ro \
-v $(pwd)/tokens:/home/appuser/.config/anilist-mal-sync \
ghcr.io/bigspawn/anilist-mal-sync:latest -c /etc/anilist-mal-sync/config.yaml syncEnable continuous sync by setting WATCH_INTERVAL environment variable:
environment:
- WATCH_INTERVAL=12h # Sync every 12 hoursOr run watch command manually:
docker-compose run --rm sync watch --interval=12hInterval limits: 1h - 168h (7 days)
Use your system's scheduler for periodic sync:
# Linux/macOS cron (daily at 2 AM)
0 2 * * * /usr/local/bin/anilist-mal-sync sync"Required environment variables not set"
- Set required env vars:
ANILIST_CLIENT_ID,ANILIST_CLIENT_SECRET,ANILIST_USERNAME,MAL_CLIENT_ID,MAL_CLIENT_SECRET,MAL_USERNAME - Or use config file with
-c /path/to/config.yaml
Authentication fails
- Check redirect URL matches exactly:
http://localhost:18080/callback - Verify client ID and secret are correct
- Ensure port 18080 is not already in use
Sync appears frozen
- Both services have rate limits. Wait a few minutes and try again
- Use
--verboseto see progress
Token expired
- Run
anilist-mal-sync statusto check - Run
anilist-mal-sync login allto reauthenticate
This project is not affiliated with AniList or MyAnimeList. Use at your own risk.
- Sync favorites
- Sync MAL to AniList
- Sync rewatching and rereading
- anime-offline-database for JSON based anime dataset
- arm-server for API anime dataset
- Hato for JSON API anime and manga
- Jikan for unofficial MyAnimeList API