diff --git a/src/AnimeClient/API/APIRequestBuilder.php b/src/AnimeClient/API/APIRequestBuilder.php
index d638935c..4d4113f9 100644
--- a/src/AnimeClient/API/APIRequestBuilder.php
+++ b/src/AnimeClient/API/APIRequestBuilder.php
@@ -79,6 +79,8 @@ abstract class APIRequestBuilder {
 	{
 		$request = (new Request($uri));
 		$request->setHeader('User-Agent', USER_AGENT);
+		$request->setTcpConnectTimeout(300000);
+		$request->setTransferTimeout(300000);
 
 		return $request;
 	}
@@ -269,7 +271,7 @@ abstract class APIRequestBuilder {
 	 */
 	public function newRequest(string $type, string $uri): self
 	{
-		if ( ! \in_array($type, $this->validMethods, TRUE))
+		if ( ! in_array($type, $this->validMethods, TRUE))
 		{
 			throw new InvalidArgumentException('Invalid HTTP method');
 		}
@@ -327,6 +329,8 @@ abstract class APIRequestBuilder {
 		$this->path = '';
 		$this->query = '';
 		$this->request = new Request($requestUrl, $type);
+		$this->request->setInactivityTimeout(300000);
+		$this->request->setTlsHandshakeTimeout(300000);
 		$this->request->setTcpConnectTimeout(300000);
 		$this->request->setTransferTimeout(300000);
 	}
diff --git a/src/AnimeClient/API/Kitsu/Model.php b/src/AnimeClient/API/Kitsu/Model.php
index 9b43dc1d..8571e30d 100644
--- a/src/AnimeClient/API/Kitsu/Model.php
+++ b/src/AnimeClient/API/Kitsu/Model.php
@@ -38,6 +38,7 @@ use Aviat\AnimeClient\API\Kitsu\Transformer\{
 	MangaTransformer,
 	MangaListTransformer
 };
+use Aviat\AnimeClient\Enum\ListType;
 use Aviat\AnimeClient\Types\{
 	Anime,
 	FormItem,
@@ -175,38 +176,6 @@ final class Model {
 		return FALSE;
 	}
 
-	/**
-	 * Retrieve the data for the anime watch history page
-	 *
-	 * @return array
-	 * @throws InvalidArgumentException
-	 * @throws Throwable
-	 */
-	public function getAnimeHistory(): array
-	{
-		$raw = $this->getRawHistoryList('anime');
-		$organized = JsonAPI::organizeData($raw);
-		$organized = array_filter($organized, fn ($item) => array_key_exists('relationships', $item));
-
-		return (new AnimeHistoryTransformer())->transform($organized);
-	}
-
-	/**
-	 * Retrieve the data for the manga read history page
-	 *
-	 * @return array
-	 * @throws InvalidArgumentException
-	 * @throws Throwable
-	 */
-	public function getMangaHistory(): array
-	{
-		$raw = $this->getRawHistoryList('manga');
-		$organized = JsonAPI::organizeData($raw);
-		$organized = array_filter($organized, fn ($item) => array_key_exists('relationships', $item));
-
-		return (new MangaHistoryTransformer())->transform($organized);
-	}
-
 	/**
 	 * Get the userid for a username from Kitsu
 	 *
@@ -420,6 +389,22 @@ final class Model {
 		return $this->animeTransformer->transform($baseData);
 	}
 
+	/**
+	 * Retrieve the data for the anime watch history page
+	 *
+	 * @return array
+	 * @throws InvalidArgumentException
+	 * @throws Throwable
+	 */
+	public function getAnimeHistory(): array
+	{
+		$raw = $this->getRawHistoryList('anime');
+		$organized = JsonAPI::organizeData($raw);
+		$organized = array_filter($organized, fn ($item) => array_key_exists('relationships', $item));
+
+		return (new AnimeHistoryTransformer())->transform($organized);
+	}
+
 	/**
 	 * Get information about a particular anime
 	 *
@@ -485,27 +470,7 @@ final class Model {
 	 */
 	public function getAnimeListCount(string $status = '') : int
 	{
-		$options = [
-			'query' => [
-				'filter' => [
-					'user_id' => $this->getUserIdByUsername(),
-					'kind' => 'anime'
-				],
-				'page' => [
-					'limit' => 1
-				],
-				'sort' => '-updated_at'
-			]
-		];
-
-		if ( ! empty($status))
-		{
-			$options['query']['filter']['status'] = $status;
-		}
-
-		$response = $this->getRequest('library-entries', $options);
-
-		return $response['meta']['count'];
+		return $this->getListCount(ListType::ANIME, $status);
 	}
 
 	/**
@@ -676,6 +641,22 @@ final class Model {
 		return $this->mangaTransformer->transform($baseData);
 	}
 
+	/**
+	 * Retrieve the data for the manga read history page
+	 *
+	 * @return array
+	 * @throws InvalidArgumentException
+	 * @throws Throwable
+	 */
+	public function getMangaHistory(): array
+	{
+		$raw = $this->getRawHistoryList('manga');
+		$organized = JsonAPI::organizeData($raw);
+		$organized = array_filter($organized, fn ($item) => array_key_exists('relationships', $item));
+
+		return (new MangaHistoryTransformer())->transform($organized);
+	}
+
 	/**
 	 * Get information about a particular manga
 	 *
@@ -754,27 +735,7 @@ final class Model {
 	 */
 	public function getMangaListCount(string $status = '') : int
 	{
-		$options = [
-			'query' => [
-				'filter' => [
-					'user_id' => $this->getUserIdByUsername(),
-					'kind' => 'manga'
-				],
-				'page' => [
-					'limit' => 1
-				],
-				'sort' => '-updated_at'
-			]
-		];
-
-		if ( ! empty($status))
-		{
-			$options['query']['filter']['status'] = $status;
-		}
-
-		$response = $this->getRequest('library-entries', $options);
-
-		return $response['meta']['count'];
+		return $this->getListCount(ListType::MANGA, $status);
 	}
 
 	/**
@@ -976,6 +937,20 @@ final class Model {
 		return $this->listItem->delete($id);
 	}
 
+	public function getSyncList(string $type): array
+	{
+		$options = [
+			'filter' => [
+				'user_id' => $this->getUserIdByUsername($this->getUsername()),
+				'kind' => $type,
+			],
+			'include' => "{$type},{$type}.mappings",
+			'sort' => '-updated_at'
+		];
+
+		return $this->getRawSyncList($type, $options);
+	}
+
 	/**
 	 * Get the aggregated pages of anime or manga history
 	 *
@@ -1124,4 +1099,93 @@ final class Model {
 		$baseData['included'] = $data['included'];
 		return $baseData;
 	}
+
+	private function getListCount(string $type, string $status = ''): int
+	{
+		$options = [
+			'query' => [
+				'filter' => [
+					'user_id' => $this->getUserIdByUsername(),
+					'kind' => $type,
+				],
+				'page' => [
+					'limit' => 1
+				],
+				'sort' => '-updated_at'
+			]
+		];
+
+		if ( ! empty($status))
+		{
+			$options['query']['filter']['status'] = $status;
+		}
+
+		$response = $this->getRequest('library-entries', $options);
+
+		return $response['meta']['count'];
+	}
+
+	/**
+	 * Get the full anime list
+	 *
+	 * @param string $type
+	 * @param array $options
+	 * @return array
+	 * @throws InvalidArgumentException
+	 * @throws Throwable
+	 */
+	private function getRawSyncList(string $type, array $options): array
+	{
+		$count = $this->getListCount($type);
+		$size = static::LIST_PAGE_SIZE;
+		$pages = ceil($count / $size);
+
+		$requester = new ParallelAPIRequest();
+
+		// Set up requests
+		for ($i = 0; $i < $pages; $i++)
+		{
+			$offset = $i * $size;
+			$requester->addRequest($this->getRawSyncListPage($type, $size, $offset, $options));
+		}
+
+		$responses = $requester->makeRequests();
+		$output = [];
+
+		foreach($responses as $response)
+		{
+			$data = Json::decode($response);
+			$output[] = $data;
+		}
+
+		return array_merge_recursive(...$output);
+	}
+
+	/**
+	 * Get the full anime list in paginated form
+	 *
+	 * @param string $type
+	 * @param int $limit
+	 * @param int $offset
+	 * @param array $options
+	 * @return Request
+	 * @throws InvalidArgumentException
+	 */
+	private function getRawSyncListPage(string $type, int $limit, int $offset = 0, array $options = []): Request
+	{
+		$defaultOptions = [
+			'filter' => [
+				'user_id' => $this->getUserIdByUsername($this->getUsername()),
+				'kind' => $type,
+			],
+			'page' => [
+				'offset' => $offset,
+				'limit' => $limit
+			],
+			'sort' => '-updated_at'
+		];
+		$options = array_merge($defaultOptions, $options);
+
+		return $this->setUpRequest('GET', 'library-entries', ['query' => $options]);
+	}
 }
\ No newline at end of file
diff --git a/src/AnimeClient/Command/BaseCommand.php b/src/AnimeClient/Command/BaseCommand.php
index 4308c00b..5f6002cf 100644
--- a/src/AnimeClient/Command/BaseCommand.php
+++ b/src/AnimeClient/Command/BaseCommand.php
@@ -43,29 +43,26 @@ abstract class BaseCommand extends Command {
 	/**
 	 * Echo text in a box
 	 *
-	 * @param string $message
+	 * @param string|array $message
 	 * @param string|int|null $fgColor
 	 * @param string|int|null $bgColor
 	 * @return void
 	 */
-	public function echoBox(string $message, $fgColor = NULL, $bgColor = NULL): void
+	public function echoBox($message, $fgColor = NULL, $bgColor = NULL): void
 	{
+		if (is_array($message))
+		{
+			$message = implode("\n", $message);
+		}
+
 		try
 		{
-			$len = strlen($message);
-
 			// color message
 			$message = Colors::colorize($message, $fgColor, $bgColor);
-			$colorLen = strlen($message);
 
 			// create the box
 			$box = new Box($this->getConsole(), $message);
 
-			if ($len !== $colorLen)
-			{
-				$box->setPadding((($colorLen - $len) / 2) + 2);
-			}
-
 			$box->write();
 
 			echo "\n";
@@ -106,6 +103,11 @@ abstract class BaseCommand extends Command {
 		$this->echoBox($message, Colors::RED | Colors::BOLD, Colors::BLACK);
 	}
 
+	public function clearLine(): void
+	{
+		$this->getConsole()->write("\r\e[2K");
+	}
+
 	/**
 	 * Setup the Di container
 	 *
diff --git a/src/AnimeClient/Command/SyncLists.php b/src/AnimeClient/Command/SyncLists.php
index b29e1226..cf1732fd 100644
--- a/src/AnimeClient/Command/SyncLists.php
+++ b/src/AnimeClient/Command/SyncLists.php
@@ -16,19 +16,18 @@
 
 namespace Aviat\AnimeClient\Command;
 
+use ConsoleKit\Widgets;
+
 use Aviat\AnimeClient\API\{
 	Anilist\MissingIdException,
 	FailedResponseException,
 	JsonAPI,
 	ParallelAPIRequest
 };
-use Aviat\AnimeClient\API\Anilist\Transformer\{
-	AnimeListTransformer as AALT,
-	MangaListTransformer as AMLT
-};
-use Aviat\AnimeClient\API\Anilist\Model as AnilistModel;
-use Aviat\AnimeClient\API\Kitsu\Model as KitsuModel;
+use Aviat\AnimeClient\API\Anilist;
+use Aviat\AnimeClient\API\Kitsu;
 use Aviat\AnimeClient\API\Mapping\{AnimeWatchingStatus, MangaReadingStatus};
+use Aviat\AnimeClient\Enum\{APISource, ListType, SyncAction};
 use Aviat\AnimeClient\Types\FormItem;
 use Aviat\Ion\Di\Exception\ContainerException;
 use Aviat\Ion\Di\Exception\NotFoundException;
@@ -44,18 +43,24 @@ final class SyncLists extends BaseCommand {
 
 	/**
 	 * Model for making requests to Anilist API
-	 * @var AnilistModel
+	 * @var Anilist\Model
 	 */
-	protected AnilistModel $anilistModel;
+	private Anilist\Model $anilistModel;
 
 	/**
 	 * Model for making requests to Kitsu API
-	 * @var KitsuModel
+	 * @var Kitsu\Model
 	 */
-	protected KitsuModel $kitsuModel;
+	private Kitsu\Model $kitsuModel;
 
 	/**
-	 * Run the Kitsu <=> Anilist sync script
+	 * Does the Kitsu API have valid authentication?
+	 * @var bool
+	 */
+	private bool $isKitsuAuthenticated = FALSE;
+
+	/**
+	 * Sync Kitsu <=> Anilist
 	 *
 	 * @param array $args
 	 * @param array $options
@@ -64,6 +69,45 @@ final class SyncLists extends BaseCommand {
 	 * @throws Throwable
 	 */
 	public function execute(array $args, array $options = []): void
+	{
+		$this->init();
+
+		foreach ([ListType::ANIME, ListType::MANGA] as $type)
+		{
+			$this->fetchCount($type);
+			$rawData = $this->fetch($type);
+			$normalized = $this->transform($type, $rawData);
+			$compared = $this->compare($type, $normalized);
+
+			/* $toUpdateCounts = [
+				'addToAnilist' => count($compared['addToAnilist']),
+				'updateAnilist' => count($compared['updateAnilist']),
+				'addToKitsu' => count($compared['addToKitsu']),
+				'updateKitsu' => count($compared['updateKitsu']),
+			];
+
+			dump($toUpdateCounts); */
+
+			$this->update($type, $compared);
+		}
+
+		/* $this->sync(ListType::ANIME);
+		$this->sync(ListType::MANGA);
+
+		$this->echoBox('Finished syncing lists'); */
+	}
+
+	// ------------------------------------------------------------------------
+	// Main sync flow methods
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Set up dependencies
+	 *
+	 * @throws ContainerException
+	 * @throws NotFoundException
+	 */
+	protected function init(): void
 	{
 		$this->setContainer($this->setupContainer());
 		$this->setCache($this->container->get('cache'));
@@ -71,28 +115,171 @@ final class SyncLists extends BaseCommand {
 		$config = $this->container->get('config');
 		$anilistEnabled = $config->get(['anilist', 'enabled']);
 
+		// We can't sync kitsu against itself!
 		if ( ! $anilistEnabled)
 		{
-			$this->echoBox('Anlist API is not enabled. Can not sync.');
-			return;
+			$this->echoErrorBox('Anlist API is not enabled. Can not sync.');
+			die();
+		}
+
+		// Authentication is required to update Kitsu
+		$this->isKitsuAuthenticated = $this->container->get('auth')->isAuthenticated();
+		if ( ! $this->isKitsuAuthenticated)
+		{
+			$this->echoWarningBox('Kitsu is not authenticated. Kitsu list can not be updated.');
 		}
 
 		$this->anilistModel = $this->container->get('anilist-model');
 		$this->kitsuModel = $this->container->get('kitsu-model');
-
-		$this->sync('anime');
-		$this->sync('manga');
-
-		$this->echoBox('Finished syncing lists');
 	}
 
 	/**
-	 * Attempt to synchronize external APIs
+	 * Get and display the count of items for each API
 	 *
 	 * @param string $type
-	 * @throws Throwable
 	 */
-	protected function sync(string $type): void
+	protected function fetchCount(string $type): void
+	{
+		$this->echo('Fetching List Counts');
+		$progress = new Widgets\ProgressBar($this->getConsole(), 2, 50, FALSE);
+
+		$displayLines = [];
+
+		$kitsuCount = $this->fetchKitsuCount($type);
+		$displayLines[] = "Number of Kitsu {$type} list items: {$kitsuCount}";
+		$progress->incr();
+
+		$anilistCount = $this->fetchAnilistCount($type);
+		$displayLines[] = "Number of Anilist {$type} list items: {$anilistCount}";
+		$progress->incr();
+
+		$this->clearLine();
+
+		$this->echoBox($displayLines);
+	}
+
+	protected function fetch(string $type): array
+	{
+		$this->echo('Fetching List Data');
+		$progress = new Widgets\ProgressBar($this->getConsole(), 2, 50, FALSE);
+
+		$anilist = $this->fetchAnilist($type);
+		$progress->incr();
+
+		$kitsu = $this->fetchKitsu($type);
+		$progress->incr();
+
+		$this->clearLine();
+
+		return [
+			'anilist' => $anilist,
+			'kitsu' => $kitsu,
+		];
+	}
+
+	protected function transform(string $type, array $data): array
+	{
+		$this->echo('Normalizing List Data');
+		$progress = new Widgets\ProgressBar($this->getConsole(), 2, 50, FALSE);
+
+		$kitsu = $this->transformKitsu($type, $data['kitsu']);
+		$progress->incr();
+
+		$anilist = $this->transformAnilist($type, $data['anilist']);
+		$progress->incr();
+
+		$this->clearLine();
+
+		return [
+			'anilist' => $anilist,
+			'kitsu' => $kitsu,
+		];
+	}
+
+	protected function compare(string $type, array $data): array
+	{
+		$this->echo('Comparing List Items');
+
+		return $this->compareLists($type, $data['anilist'], $data['kitsu']);
+	}
+
+	protected function update(string $type, array $data)
+	{
+		if ( ! empty($data['addToAnilist']))
+		{
+			$count = count($data['addToAnilist']);
+			$this->echoBox("Adding {$count} missing {$type} list items to Anilist");
+			$this->updateAnilistListItems($data['addToAnilist'], SyncAction::CREATE, $type);
+		}
+
+		if ( ! empty($data['updateAnilist']))
+		{
+			$count = count($data['updateAnilist']);
+			$this->echoBox("Updating {$count} outdated Anilist {$type} list items");
+			$this->updateAnilistListItems($data['updateAnilist'], SyncAction::UPDATE, $type);
+		}
+
+		if ($this->isKitsuAuthenticated)
+		{
+			if ( ! empty($data['addToKitsu']))
+			{
+				$count = count($data['addToKitsu']);
+				$this->echoBox("Adding {$count} missing {$type} list items to Kitsu");
+				$this->updateKitsuListItems($data['addToKitsu'], SyncAction::CREATE, $type);
+			}
+
+			if ( ! empty($data['updateKitsu']))
+			{
+				$count = count($data['updateKitsu']);
+				$this->echoBox("Updating {$count} outdated Kitsu {$type} list items");
+				$this->updateKitsuListItems($data['updateKitsu'], SyncAction::UPDATE, $type);
+			}
+		}
+		else
+		{
+			$this->echoErrorBox('Kitsu is not authenticated, so lists can not be updated');
+		}
+	}
+
+	// ------------------------------------------------------------------------
+	// Fetch helpers
+	// ------------------------------------------------------------------------
+	private function fetchAnilistCount(string $type)
+	{
+		$list = $this->fetchAnilist($type);
+
+		if ( ! isset($list['data']['MediaListCollection']['lists']))
+		{
+			return 0;
+		}
+
+		$count = 0;
+
+		foreach ($list['data']['MediaListCollection']['lists'] as $subList)
+		{
+			$count += array_reduce($subList, fn ($carry, $item) => $carry + count(array_values($item)), 0);
+		}
+
+		return $count;
+	}
+
+	private function fetchAnilist(string $type): array
+	{
+		static $list = [
+			ListType::ANIME => NULL,
+			ListType::MANGA => NULL,
+		];
+
+		// This uses a static so I don't have to fetch this list twice for a count
+		if ($list[$type] === NULL)
+		{
+			$list[$type] = $this->anilistModel->getSyncList(strtoupper($type));
+		}
+
+		return $list[$type];
+	}
+
+	private function fetchKitsuCount(string $type): int
 	{
 		$uType = ucfirst($type);
 
@@ -106,157 +293,31 @@ final class SyncLists extends BaseCommand {
 			dump($e);
 		}
 
-
-		$this->echoBox("Number of Kitsu {$type} list items: {$kitsuCount}");
-
-		$data = $this->diffLists($type);
-
-		if ( ! empty($data['addToAnilist']))
-		{
-			$count = count($data['addToAnilist']);
-			$this->echoBox("Adding {$count} missing {$type} list items to Anilist");
-			$this->updateAnilistListItems($data['addToAnilist'], 'create', $type);
-		}
-
-		if ( ! empty($data['updateAnilist']))
-		{
-			$count = count($data['updateAnilist']);
-			$this->echoBox("Updating {$count} outdated Anilist {$type} list items");
-			$this->updateAnilistListItems($data['updateAnilist'], 'update', $type);
-		}
-
-		if ( ! empty($data['addToKitsu']))
-		{
-			$count = count($data['addToKitsu']);
-			$this->echoBox("Adding {$count} missing {$type} list items to Kitsu");
-			$this->updateKitsuListItems($data['addToKitsu'], 'create', $type);
-		}
-
-		if ( ! empty($data['updateKitsu']))
-		{
-			$count = count($data['updateKitsu']);
-			$this->echoBox("Updating {$count} outdated Kitsu {$type} list items");
-			$this->updateKitsuListItems($data['updateKitsu'], 'update', $type);
-		}
+		return $kitsuCount;
 	}
 
-	/**
-	 * Filter Kitsu mappings for the specified type
-	 *
-	 * @param array $includes
-	 * @param string $type
-	 * @return array
-	 */
-	protected function filterMappings(array $includes, string $type = 'anime'): array
+	private function fetchKitsu(string $type): array
 	{
-		$output = [];
-
-		foreach($includes as $id => $mapping)
-		{
-			if ($mapping['externalSite'] === "myanimelist/{$type}")
-			{
-				$output[$id] = $mapping;
-			}
-		}
-
-		return $output;
+		return $this->kitsuModel->getSyncList($type);
 	}
 
-	/**
-	 * Format an Anilist list for comparison
-	 *
-	 * @param string $type
-	 * @return array
-	 */
-	protected function formatAnilistList(string $type): array
+	// ------------------------------------------------------------------------
+	// Transform Helpers
+	// ------------------------------------------------------------------------
+
+	private function transformKitsu(string $type, array $data): array
 	{
-		$type = ucfirst($type);
-		$method = "formatAnilist{$type}List";
-		return $this->$method();
-	}
-
-	/**
-	 * Format an Anilist anime list for comparison
-	 *
-	 * @return array
-	 * @throws ContainerException
-	 * @throws NotFoundException
-	 */
-	protected function formatAnilistAnimeList(): array
-	{
-		$anilistList = $this->anilistModel->getSyncList('ANIME');
-		$anilistTransformer = new AALT();
-
-		$transformedAnilist = [];
-
-		foreach ($anilistList['data']['MediaListCollection']['lists'] as $list)
-		{
-			$newTransformed = $anilistTransformer->untransformCollection($list['entries']);
-			$transformedAnilist = array_merge($transformedAnilist, $newTransformed);
-		}
-
-		// Key the array by the mal_id for easier reference in the next comparision step
-		$output = [];
-		foreach ($transformedAnilist as $item)
-		{
-			$output[$item['mal_id']] = $item->toArray();
-		}
-
-		$count = count($output);
-		$this->echoBox("Number of Anilist anime list items: {$count}");
-
-		return $output;
-	}
-
-	/**
-	 * Format an Anilist manga list for comparison
-	 *
-	 * @return array
-	 * @throws ContainerException
-	 * @throws NotFoundException
-	 */
-	protected function formatAnilistMangaList(): array
-	{
-		$anilistList = $this->anilistModel->getSyncList('MANGA');
-		$anilistTransformer = new AMLT();
-
-		$transformedAnilist = [];
-
-		foreach ($anilistList['data']['MediaListCollection']['lists'] as $list)
-		{
-			$newTransformed = $anilistTransformer->untransformCollection($list['entries']);
-			$transformedAnilist = array_merge($transformedAnilist, $newTransformed);
-		}
-
-		// Key the array by the mal_id for easier reference in the next comparision step
-		$output = [];
-		foreach ($transformedAnilist as $item)
-		{
-			$output[$item['mal_id']] = $item->toArray();
-		}
-
-		$count = count($output);
-		$this->echoBox("Number of Anilist manga list items: {$count}");
-
-		return $output;
-	}
-
-	/**
-	 * Format a kitsu list for the sake of comparision
-	 *
-	 * @param string $type
-	 * @return array
-	 */
-	protected function formatKitsuList(string $type = 'anime'): array
-	{
-		$method = 'getFullRaw' . ucfirst($type) . 'List';
-		$data = $this->kitsuModel->$method();
-
 		if (empty($data))
 		{
 			return [];
 		}
 
+		if ( ! array_key_exists('included', $data))
+		{
+			dump($data);
+			return [];
+		}
+
 		$includes = JsonAPI::organizeIncludes($data['included']);
 		$includes['mappings'] = $this->filterMappings($includes['mappings'], $type);
 
@@ -271,7 +332,7 @@ final class SyncLists extends BaseCommand {
 
 			foreach ($potentialMappings as $mappingId)
 			{
-				if (\is_array($mappingId))
+				if (is_array($mappingId))
 				{
 					continue;
 				}
@@ -298,21 +359,37 @@ final class SyncLists extends BaseCommand {
 		return $output;
 	}
 
-	/**
-	 * Go through lists of the specified type, and determine what kind of action each item needs
-	 *
-	 * @param string $type
-	 * @return array
-	 */
-	protected function diffLists(string $type = 'anime'): array
+	private function transformAnilist(string $type, array $data): array
 	{
-		// Get libraryEntries with media.mappings from Kitsu
-		// Organize mappings, and ignore entries without mappings
-		$kitsuList = $this->formatKitsuList($type);
+		$uType = ucfirst($type);
+		$className = "\\Aviat\\AnimeClient\\API\\Anilist\\Transformer\\{$uType}ListTransformer";
+		$transformer = new $className;
 
-		// Get Anilist list data
-		$anilistList = $this->formatAnilistList($type);
+		$firstTransformed = [];
 
+		foreach ($data['data']['MediaListCollection']['lists'] as $list)
+		{
+			$firstTransformed[] = $transformer->untransformCollection($list['entries']);
+		}
+
+		$transformed = array_merge_recursive(...$firstTransformed);
+
+		// Key the array by mal_id
+		$output = [];
+		foreach ($transformed as $item)
+		{
+			$output[$item['mal_id']] = $item->toArray();
+		}
+
+		return $output;
+	}
+
+	// ------------------------------------------------------------------------
+	// Compare Helpers
+	// ------------------------------------------------------------------------
+
+	private function compareLists(string $type, array $anilistList, array $kitsuList): array
+	{
 		$itemsToAddToAnilist = [];
 		$itemsToAddToKitsu = [];
 		$anilistUpdateItems = [];
@@ -320,15 +397,21 @@ final class SyncLists extends BaseCommand {
 
 		$malIds = array_keys($anilistList);
 		$kitsuMalIds = array_map('intval', array_column($kitsuList, 'malId'));
-		$missingMalIds = array_diff($malIds, $kitsuMalIds);
+		$missingMalIds = array_filter(array_diff($kitsuMalIds, $malIds), fn ($id) => ! in_array($id, $kitsuMalIds));
 
 		// Add items on Anilist, but not Kitsu to Kitsu
 		foreach($missingMalIds as $mid)
 		{
-			$itemsToAddToKitsu[] = array_merge($anilistList[$mid]['data'], [
-				'id' => $this->kitsuModel->getKitsuIdFromMALId((string)$mid, $type),
-				'type' => $type
-			]);
+			if ( ! array_key_exists($mid, $anilistList))
+			{
+				continue;
+			}
+
+			$data = $anilistList[$mid]['data'];
+			$data['id'] = $this->kitsuModel->getKitsuIdFromMALId((string)$mid, $type);
+			$data['type'] = $type;
+
+			$itemsToAddToKitsu[] = $data;
 		}
 
 		foreach($kitsuList as $kitsuItem)
@@ -359,7 +442,7 @@ final class SyncLists extends BaseCommand {
 				continue;
 			}
 
-			$statusMap = ($type === 'anime') ? AnimeWatchingStatus::class : MangaReadingStatus::class;
+			$statusMap = ($type === ListType::ANIME) ? AnimeWatchingStatus::class : MangaReadingStatus::class;
 
 			// Looks like this item only exists on Kitsu
 			$kItem = $kitsuItem['data'];
@@ -392,7 +475,7 @@ final class SyncLists extends BaseCommand {
 	 * @param array $anilistItem
 	 * @return array|null
 	 */
-	protected function compareListItems(array $kitsuItem, array $anilistItem): ?array
+	private function compareListItems(array $kitsuItem, array $anilistItem): ?array
 	{
 		$compareKeys = [
 			'notes',
@@ -585,6 +668,10 @@ final class SyncLists extends BaseCommand {
 		return $return;
 	}
 
+	// ------------------------------------------------------------------------
+	// Update Helpers
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Create/Update list items on Kitsu
 	 *
@@ -593,23 +680,23 @@ final class SyncLists extends BaseCommand {
 	 * @param string $type
 	 * @throws Throwable
 	 */
-	protected function updateKitsuListItems(array $itemsToUpdate, string $action = 'update', string $type = 'anime'): void
+	private function updateKitsuListItems(array $itemsToUpdate, string $action = SyncAction::UPDATE, string $type = ListType::ANIME): void
 	{
 		$requester = new ParallelAPIRequest();
 		foreach($itemsToUpdate as $item)
 		{
-			if ($action === 'update')
+			if ($action === SyncAction::UPDATE)
 			{
 				$requester->addRequest(
 					$this->kitsuModel->updateListItem(FormItem::from($item))
 				);
 			}
-			else if ($action === 'create')
+			else if ($action === SyncAction::CREATE)
 			{
 				$maybeRequest = $this->kitsuModel->createListItem($item);
 				if ($maybeRequest === NULL)
 				{
-					$this->echoBox("Skipped creating Kitsu {$type} due to missing id ¯\_(ツ)_/¯");
+					$this->echoWarning("Skipped creating Kitsu {$type} due to missing id ¯\_(ツ)_/¯");
 					continue;
 				}
 				$requester->addRequest($this->kitsuModel->createListItem($item));
@@ -625,8 +712,8 @@ final class SyncLists extends BaseCommand {
 			$id = $itemsToUpdate[$key]['id'];
 			if ( ! array_key_exists('errors', $responseData))
 			{
-				$verb = ($action === 'update') ? 'updated' : 'created';
-				$this->echoBox("Successfully {$verb} Kitsu {$type} list item with id: {$id}");
+				$verb = ($action === SyncAction::UPDATE) ? 'updated' : 'created';
+				$this->echoSuccess("Successfully {$verb} Kitsu {$type} list item with id: {$id}");
 				continue;
 			}
 
@@ -637,14 +724,14 @@ final class SyncLists extends BaseCommand {
 
 				if ($errorTitle === 'cannot exceed length of media')
 				{
-					$this->echoBox("Skipped Kitsu {$type} {$id} due to episode count mismatch with other API");
+					$this->echoWarning("Skipped Kitsu {$type} {$id} due to episode count mismatch with other API");
 					continue;
 				}
 			}
 
 			dump($responseData);
-			$verb = ($action === 'update') ? 'update' : 'create';
-			$this->echoBox("Failed to {$verb} Kitsu {$type} list item with id: {$id}");
+			$verb = ($action === SyncAction::UPDATE) ? SyncAction::UPDATE : SyncAction::CREATE;
+			$this->echoError("Failed to {$verb} Kitsu {$type} list item with id: {$id}");
 
 		}
 	}
@@ -657,19 +744,19 @@ final class SyncLists extends BaseCommand {
 	 * @param string $type
 	 * @throws Throwable
 	 */
-	protected function updateAnilistListItems(array $itemsToUpdate, string $action = 'update', string $type = 'anime'): void
+	private function updateAnilistListItems(array $itemsToUpdate, string $action = SyncAction::UPDATE, string $type = ListType::ANIME): void
 	{
 		$requester = new ParallelAPIRequest();
 
 		foreach($itemsToUpdate as $item)
 		{
-			if ($action === 'update')
+			if ($action === SyncAction::UPDATE)
 			{
 				$requester->addRequest(
 					$this->anilistModel->updateListItem(FormItem::from($item), $type)
 				);
 			}
-			else if ($action === 'create')
+			else if ($action === SyncAction::CREATE)
 			{
 				try
 				{
@@ -679,7 +766,7 @@ final class SyncLists extends BaseCommand {
 				{
 					// Case where there's a MAL mapping from Kitsu, but no equivalent Anlist item
 					$id = $item['mal_id'];
-					$this->echoBox("Skipping Anilist ${type} with mal_id: {$id} due to missing mapping");
+					$this->echoWarning("Skipping Anilist ${type} with MAL id: {$id} due to missing mapping");
 				}
 			}
 		}
@@ -694,15 +781,41 @@ final class SyncLists extends BaseCommand {
 
 			if ( ! array_key_exists('errors', $responseData))
 			{
-				$verb = ($action === 'update') ? 'updated' : 'created';
-				$this->echoBox("Successfully {$verb} Anilist {$type} list item with id: {$id}");
+				$verb = ($action === SyncAction::UPDATE) ? 'updated' : 'created';
+				$this->echoSuccess("Successfully {$verb} Anilist {$type} list item with id: {$id}");
 			}
 			else
 			{
 				dump($responseData);
-				$verb = ($action === 'update') ? 'update' : 'create';
-				$this->echoBox("Failed to {$verb} Anilist {$type} list item with id: {$id}");
+				$verb = ($action === SyncAction::UPDATE) ? SyncAction::UPDATE : SyncAction::CREATE;
+				$this->echoError("Failed to {$verb} Anilist {$type} list item with id: {$id}");
 			}
 		}
 	}
+
+	// ------------------------------------------------------------------------
+	// Other Helpers
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Filter Kitsu mappings for the specified type
+	 *
+	 * @param array $includes
+	 * @param string $type
+	 * @return array
+	 */
+	private function filterMappings(array $includes, string $type = ListType::ANIME): array
+	{
+		$output = [];
+
+		foreach($includes as $id => $mapping)
+		{
+			if ($mapping['externalSite'] === "myanimelist/{$type}")
+			{
+				$output[$id] = $mapping;
+			}
+		}
+
+		return $output;
+	}
 }
diff --git a/src/AnimeClient/Types/FormItemData.php b/src/AnimeClient/Types/FormItemData.php
index 1058d0e8..e9956a92 100644
--- a/src/AnimeClient/Types/FormItemData.php
+++ b/src/AnimeClient/Types/FormItemData.php
@@ -28,7 +28,7 @@ class FormItemData extends AbstractType {
 	/**
 	 * @var bool
 	 */
-	public bool $private = FALSE;
+	public ?bool $private = FALSE;
 
 	/**
 	 * @var int