diff --git a/src/en/lynxscans/AndroidManifest.xml b/src/en/lynxscans/AndroidManifest.xml
new file mode 100644
index 000000000..30deb7f79
--- /dev/null
+++ b/src/en/lynxscans/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/src/en/lynxscans/build.gradle b/src/en/lynxscans/build.gradle
new file mode 100644
index 000000000..4b62f029f
--- /dev/null
+++ b/src/en/lynxscans/build.gradle
@@ -0,0 +1,12 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlinx-serialization'
+
+ext {
+ extName = 'LynxScans'
+ pkgNameSuffix = 'en.lynxscans'
+ extClass = '.LynxScans'
+ extVersionCode = 7
+}
+
+apply from: "$rootDir/common.gradle"
diff --git a/src/en/lynxscans/res/mipmap-hdpi/ic_launcher.png b/src/en/lynxscans/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 000000000..1dd6cfde4
Binary files /dev/null and b/src/en/lynxscans/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/src/en/lynxscans/res/mipmap-mdpi/ic_launcher.png b/src/en/lynxscans/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 000000000..1c411bc85
Binary files /dev/null and b/src/en/lynxscans/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/src/en/lynxscans/res/mipmap-xhdpi/ic_launcher.png b/src/en/lynxscans/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..f9083e6c8
Binary files /dev/null and b/src/en/lynxscans/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/src/en/lynxscans/res/mipmap-xxhdpi/ic_launcher.png b/src/en/lynxscans/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..951a16c8f
Binary files /dev/null and b/src/en/lynxscans/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/src/en/lynxscans/res/mipmap-xxxhdpi/ic_launcher.png b/src/en/lynxscans/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 000000000..5e87a4ede
Binary files /dev/null and b/src/en/lynxscans/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/src/en/lynxscans/res/web_hi_res_512.png b/src/en/lynxscans/res/web_hi_res_512.png
new file mode 100644
index 000000000..65950d349
Binary files /dev/null and b/src/en/lynxscans/res/web_hi_res_512.png differ
diff --git a/src/en/lynxscans/src/eu/kanade/tachiyomi/extension/en/lynxscans/LynxScans.kt b/src/en/lynxscans/src/eu/kanade/tachiyomi/extension/en/lynxscans/LynxScans.kt
new file mode 100644
index 000000000..a151a0a20
--- /dev/null
+++ b/src/en/lynxscans/src/eu/kanade/tachiyomi/extension/en/lynxscans/LynxScans.kt
@@ -0,0 +1,130 @@
+package eu.kanade.tachiyomi.extension.en.lynxscans
+
+import eu.kanade.tachiyomi.network.GET
+import eu.kanade.tachiyomi.source.model.FilterList
+import eu.kanade.tachiyomi.source.model.MangasPage
+import eu.kanade.tachiyomi.source.model.Page
+import eu.kanade.tachiyomi.source.model.SChapter
+import eu.kanade.tachiyomi.source.model.SManga
+import eu.kanade.tachiyomi.source.online.HttpSource
+import kotlinx.serialization.decodeFromString
+import kotlinx.serialization.json.Json
+import okhttp3.Request
+import okhttp3.Response
+import uy.kohesive.injekt.injectLazy
+
+class LynxScans : HttpSource() {
+ override val baseUrl: String = "https://lynxscans.com"
+ private val apiUrl: String = "https://api.lynxscans.com/api"
+
+ override val lang: String = "en"
+ override val name: String = "LynxScans"
+
+ override val versionId = 2
+
+ override val supportsLatest: Boolean = true
+
+ private val json: Json by injectLazy()
+
+ // Popular
+ override fun popularMangaRequest(page: Int): Request {
+ return GET("$apiUrl/comics?page=$page", headers)
+ }
+
+ override fun popularMangaParse(response: Response): MangasPage {
+ val data = json.decodeFromString(response.body.string())
+
+ val titles = data.comics.data.map(PopularComicsData::toSManga)
+
+ return MangasPage(titles, !data.comics.next_page_url.isNullOrEmpty())
+ }
+
+ // Latest
+ override fun latestUpdatesRequest(page: Int): Request {
+ return GET("$apiUrl/latest?page=$page", headers)
+ }
+
+ override fun latestUpdatesParse(response: Response): MangasPage {
+ val data = json.decodeFromString(response.body.string())
+
+ val titles = data.chapters.data.distinctBy { it.comic_titleSlug }.map(LatestChaptersData::toSManga)
+
+ return MangasPage(titles, !data.chapters.next_page_url.isNullOrEmpty())
+ }
+
+ // Search
+ override fun searchMangaParse(response: Response): MangasPage {
+ throw UnsupportedOperationException("Not used")
+ }
+
+ override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
+ throw Exception("Search is not supported")
+ }
+
+ // Details
+ override fun mangaDetailsParse(response: Response): SManga {
+ val data = json.decodeFromString(response.body.string())
+
+ return data.comic.toSManga()
+ }
+
+ override fun mangaDetailsRequest(manga: SManga): Request {
+ val titleId = manga.url.substringAfterLast("/")
+
+ return GET("$apiUrl/comics/$titleId", headers)
+ }
+
+ override fun getMangaUrl(manga: SManga): String {
+ val titleId = manga.url.substringAfterLast("/")
+
+ return "$baseUrl/comics/$titleId"
+ }
+
+ // Chapters
+ override fun chapterListRequest(manga: SManga): Request {
+ return mangaDetailsRequest(manga)
+ }
+
+ override fun chapterListParse(response: Response): List {
+ val data = json.decodeFromString(response.body.string())
+
+ val chapters: MutableList = mutableListOf()
+
+ data.comic.volumes.forEach { volume ->
+ volume.chapters.forEach { chapter ->
+ chapters.add(
+ SChapter.create().apply {
+ url = "/comics/${data.comic.titleSlug}/volume/${volume.number}/chapter/${chapter.number}"
+ name = volume.name + " " + (if (!chapter.name.contains("chapter", true)) "Chapter ${chapter.number} " else "") + chapter.name
+ },
+ )
+ }
+ }
+
+ return chapters
+ }
+
+ override fun getChapterUrl(chapter: SChapter): String {
+ val chapterPath = chapter.url.substringAfter("/")
+
+ return "$baseUrl/$chapterPath"
+ }
+
+ // Page
+ override fun pageListRequest(chapter: SChapter): Request {
+ val chapterPath = chapter.url.substringAfter("/")
+
+ return GET("$apiUrl/$chapterPath", headers)
+ }
+
+ override fun pageListParse(response: Response): List {
+ val data = json.decodeFromString(response.body.string())
+
+ return data.pages.mapIndexed { idx, it ->
+ Page(idx, imageUrl = it.thumb)
+ }
+ }
+
+ // Unused
+ override fun imageUrlParse(response: Response): String = throw UnsupportedOperationException("Not Used")
+}
diff --git a/src/en/lynxscans/src/eu/kanade/tachiyomi/extension/en/lynxscans/LynxScansDto.kt b/src/en/lynxscans/src/eu/kanade/tachiyomi/extension/en/lynxscans/LynxScansDto.kt
new file mode 100644
index 000000000..81780f5c5
--- /dev/null
+++ b/src/en/lynxscans/src/eu/kanade/tachiyomi/extension/en/lynxscans/LynxScansDto.kt
@@ -0,0 +1,110 @@
+package eu.kanade.tachiyomi.extension.en.lynxscans
+
+import eu.kanade.tachiyomi.source.model.SManga
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class Latest(
+ val chapters: LatestChapters,
+)
+
+@Serializable
+data class LatestChapters(
+ val next_page_url: String?,
+ val data: List,
+)
+
+@Serializable
+data class LatestChaptersData(
+ val comic_title: String,
+ val comic_thumb: String,
+ val comic_titleSlug: String,
+) {
+ fun toSManga(): SManga = SManga.create().apply {
+ title = this@LatestChaptersData.comic_title
+ thumbnail_url = this@LatestChaptersData.comic_thumb
+ url = "/comics/" + this@LatestChaptersData.comic_titleSlug
+ }
+}
+
+@Serializable
+data class Popular(
+ val comics: PopularComics,
+)
+
+@Serializable
+data class PopularComics(
+ val next_page_url: String?,
+ val data: List,
+)
+
+@Serializable
+data class PopularComicsData(
+ val title: String,
+ val thumb: String,
+ val titleSlug: String,
+) {
+ fun toSManga(): SManga = SManga.create().apply {
+ title = this@PopularComicsData.title
+ thumbnail_url = this@PopularComicsData.thumb
+ url = "/comics/" + this@PopularComicsData.titleSlug
+ }
+}
+
+@Serializable
+data class MangaDetails(
+ val comic: MangaDetailsComicData,
+)
+
+@Serializable
+data class MangaDetailsComicData(
+ val title: String,
+ val thumb: String,
+ val titleSlug: String,
+ val artist: String,
+ val author: String,
+ val description: String,
+ val tags: List,
+
+ val volumes: List,
+
+) {
+ fun toSManga(): SManga = SManga.create().apply {
+ title = this@MangaDetailsComicData.title
+ thumbnail_url = this@MangaDetailsComicData.thumb
+ url = "/comics/" + this@MangaDetailsComicData.titleSlug
+ author = if (this@MangaDetailsComicData.author != "blank") this@MangaDetailsComicData.author else null
+ artist = if (this@MangaDetailsComicData.artist != "blank") this@MangaDetailsComicData.artist else null
+ description = this@MangaDetailsComicData.description
+ genre = this@MangaDetailsComicData.tags.joinToString { it.name }
+ status = SManga.UNKNOWN
+ }
+}
+
+@Serializable
+data class MangaDetailsTag(
+ val name: String,
+)
+
+@Serializable
+data class MangaDetailsVolume(
+ val chapters: List,
+ val name: String,
+ val number: Int,
+)
+
+@Serializable
+data class MangaDetailsChapter(
+ val name: String,
+ val number: Int,
+)
+
+@Serializable
+data class PageList(
+ val pages: List,
+)
+
+@Serializable
+data class Page(
+ val thumb: String,
+)