diff --git a/src/es/akaya/build.gradle b/src/es/akaya/build.gradle
new file mode 100644
index 000000000..eaa9c7821
--- /dev/null
+++ b/src/es/akaya/build.gradle
@@ -0,0 +1,8 @@
+ext {
+    extName = 'AKAYA'
+    extClass = '.Akaya'
+    extVersionCode = 1
+    isNsfw = true
+}
+
+apply from: "$rootDir/common.gradle"
diff --git a/src/es/akaya/res/mipmap-hdpi/ic_launcher.png b/src/es/akaya/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 000000000..bbd945bfd
Binary files /dev/null and b/src/es/akaya/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/src/es/akaya/res/mipmap-mdpi/ic_launcher.png b/src/es/akaya/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 000000000..2b9deab67
Binary files /dev/null and b/src/es/akaya/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/src/es/akaya/res/mipmap-xhdpi/ic_launcher.png b/src/es/akaya/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..fd55393e5
Binary files /dev/null and b/src/es/akaya/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/src/es/akaya/res/mipmap-xxhdpi/ic_launcher.png b/src/es/akaya/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..af68d0a59
Binary files /dev/null and b/src/es/akaya/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/src/es/akaya/res/mipmap-xxxhdpi/ic_launcher.png b/src/es/akaya/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 000000000..9ddac802a
Binary files /dev/null and b/src/es/akaya/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/src/es/akaya/src/eu/kanade/tachiyomi/extension/es/akaya/Akaya.kt b/src/es/akaya/src/eu/kanade/tachiyomi/extension/es/akaya/Akaya.kt
new file mode 100644
index 000000000..8ccf43ac6
--- /dev/null
+++ b/src/es/akaya/src/eu/kanade/tachiyomi/extension/es/akaya/Akaya.kt
@@ -0,0 +1,220 @@
+package eu.kanade.tachiyomi.extension.es.akaya
+
+import eu.kanade.tachiyomi.network.GET
+import eu.kanade.tachiyomi.network.POST
+import eu.kanade.tachiyomi.network.asObservableSuccess
+import eu.kanade.tachiyomi.network.interceptor.rateLimitHost
+import eu.kanade.tachiyomi.source.model.Filter
+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.ParsedHttpSource
+import eu.kanade.tachiyomi.util.asJsoup
+import okhttp3.FormBody
+import okhttp3.HttpUrl.Companion.toHttpUrl
+import okhttp3.Request
+import okhttp3.Response
+import org.jsoup.nodes.Document
+import org.jsoup.nodes.Element
+import rx.Observable
+import java.io.IOException
+import java.text.ParseException
+import java.text.SimpleDateFormat
+import java.util.Locale
+
+class Akaya : ParsedHttpSource() {
+
+    override val name: String = "AKAYA"
+
+    override val baseUrl: String = "https://akaya.io"
+
+    override val lang: String = "es"
+
+    override val supportsLatest: Boolean = true
+
+    override val client = network.cloudflareClient.newBuilder()
+        .rateLimitHost(baseUrl.toHttpUrl(), 1, 1)
+        .addInterceptor { chain ->
+            val request = chain.request()
+            if (!request.url.toString().startsWith("$baseUrl/serie")) return@addInterceptor chain.proceed(request)
+            val response = chain.proceed(request)
+            if (response.request.url.toString().removeSuffix("/") == baseUrl) {
+                throw IOException("Esta serie no se encuentra disponible")
+            }
+            return@addInterceptor response
+        }
+        .addInterceptor { chain ->
+            val request = chain.request()
+            if (!request.url.toString().startsWith("$baseUrl/search")) return@addInterceptor chain.proceed(request)
+            val query = request.url.fragment!!
+            if (csrfToken.isEmpty()) getCsrftoken()
+            val response = chain.proceed(addFormBody(request, query))
+            if (response.code == 419) {
+                response.close()
+                getCsrftoken()
+                return@addInterceptor chain.proceed(addFormBody(request, query))
+            }
+            return@addInterceptor response
+        }
+        .build()
+
+    private fun addFormBody(request: Request, query: String): Request {
+        val body = FormBody.Builder()
+            .add("_token", csrfToken)
+            .add("search", query)
+            .build()
+
+        return request.newBuilder()
+            .url(request.url.toString().substringBefore("#"))
+            .post(body)
+            .build()
+    }
+
+    override fun headersBuilder() = super.headersBuilder()
+        .set("Referer", "$baseUrl/")
+
+    override fun popularMangaRequest(page: Int): Request =
+        GET("$baseUrl/collection/bd90cb43-9bf2-4759-b8cc-c9e66a526bc6?page=$page", headers)
+
+    override fun popularMangaSelector() = searchMangaSelector()
+
+    override fun popularMangaNextPageSelector() = searchMangaNextPageSelector()
+
+    override fun popularMangaFromElement(element: Element) = searchMangaFromElement(element)
+
+    override fun latestUpdatesRequest(page: Int): Request =
+        GET("$baseUrl/collection/0031a504-706c-4666-9782-a4ae30cad973?page=$page", headers)
+
+    override fun latestUpdatesSelector() = popularMangaSelector()
+
+    override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector()
+
+    override fun latestUpdatesFromElement(element: Element) = popularMangaFromElement(element)
+
+    private var csrfToken: String = ""
+
+    override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
+        if (query.isEmpty()) return super.fetchSearchManga(page, query, filters)
+        return client.newCall(querySearchMangaRequest(query)).asObservableSuccess().map { response ->
+            querySearchMangaParse(response)
+        }
+    }
+
+    override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
+        val url = baseUrl.toHttpUrl().newBuilder()
+
+        val order = filters.filterIsInstance<OrderFilter>().first().toUriPart()
+        val genres = filters.filterIsInstance<GenreFilter>().first().state
+            .filter(Genre::state)
+            .map(Genre::id)
+
+        url.addPathSegment(order)
+        if (genres.isNotEmpty()) url.addPathSegment(genres.joinToString(",", "[", "]"))
+
+        url.addQueryParameter("page", page.toString())
+
+        return GET(url.build(), headers)
+    }
+
+    private fun getCsrftoken() {
+        val response = client.newCall(GET(baseUrl, headers)).execute()
+        val document = response.asJsoup()
+        csrfToken = document.selectFirst("meta[name=csrf-token]")!!.attr("content")
+    }
+
+    private fun querySearchMangaRequest(query: String): Request = POST("$baseUrl/search#$query", headers)
+
+    private fun querySearchMangaParse(response: Response): MangasPage {
+        val document = response.asJsoup()
+        val mangas = document.select("main > div.search-title > div.rowDiv div.list-search:has(div.inner-img-search)").map {
+            SManga.create().apply {
+                setUrlWithoutDomain(it.selectFirst("div.name-serie-search > a")!!.attr("href"))
+                thumbnail_url = it.selectFirst("div.inner-img-search")!!.attr("style")
+                    .substringAfter("url(").substringBefore(")")
+                title = it.select("div.name-serie-search").text()
+            }
+        }
+
+        return MangasPage(mangas, false)
+    }
+
+    override fun getFilterList() = FilterList(
+        Filter.Header("Los filtros se ignorarán al hacer una búsqueda por texto"),
+        Filter.Separator(),
+        OrderFilter(),
+        GenreFilter(),
+    )
+
+    override fun searchMangaSelector() = "div.serie_items > div.library-grid-item"
+
+    override fun searchMangaNextPageSelector() = "div.wrapper-navigation ul.pagination > li > a[rel=next]"
+
+    override fun searchMangaFromElement(element: Element) = SManga.create().apply {
+        setUrlWithoutDomain(element.selectFirst("a")!!.attr("href"))
+        title = element.selectFirst("span > h5 > strong")!!.text()
+        thumbnail_url = element.selectFirst("div.inner-img")?.attr("style")
+            ?.substringAfter("url(")?.substringBefore(")")
+            ?: element.selectFirst("div.img-fluid")?.attr("abs:src")
+    }
+
+    override fun mangaDetailsParse(document: Document) = SManga.create().apply {
+        with(document.selectFirst("header.masthead > div.container > div.row")!!) {
+            title = selectFirst(".serie-head-title")!!.text()
+            author = selectFirst("ul.persons")!!.let { element ->
+                element.select("li").joinToString { it.text() }
+                    .ifEmpty { element.text() }
+            }
+            genre = selectFirst("ul.categories")!!.let { element ->
+                element.select("li").joinToString { it.text() }
+                    .ifEmpty { element.text() }
+            }
+        }
+        thumbnail_url = document.selectFirst("meta[property=og:image]")!!.attr("content")
+            .replace("/chapters/", "/content/")
+        description = document.selectFirst("section.main div.container div.sidebar > p")!!.text()
+    }
+
+    override fun chapterListRequest(manga: SManga): Request =
+        GET(baseUrl + manga.url + "?order_direction=desc", headers)
+
+    override fun chapterListSelector() = "div.chapter-desktop div.chapter-item"
+
+    override fun chapterFromElement(element: Element) = SChapter.create().apply {
+        setUrlWithoutDomain(element.selectFirst("div.text-left > .mt-1 > a")!!.attr("href"))
+        name = element.selectFirst("div.text-left > .mt-1 > a")!!.text()
+        date_upload = parseDate(element.selectFirst("p.date")!!.text())
+
+        element.selectFirst("i.ak-lock")?.let {
+            name = "🔒 $name"
+            url = "$url#lock"
+        }
+    }
+
+    private val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale("es"))
+
+    private fun parseDate(date: String): Long {
+        return try {
+            dateFormat.parse(date)?.time ?: 0L
+        } catch (e: ParseException) {
+            0L
+        }
+    }
+
+    override fun pageListRequest(chapter: SChapter): Request {
+        if (chapter.url.substringAfterLast("#") == "lock") {
+            throw Exception("Capítulo bloqueado")
+        }
+
+        return super.pageListRequest(chapter)
+    }
+
+    override fun pageListParse(document: Document): List<Page> {
+        return document.select("main.separatorReading div.container img.img-fluid").mapIndexed { i, img ->
+            Page(i, imageUrl = img.attr("abs:src"))
+        }
+    }
+
+    override fun imageUrlParse(document: Document) = throw UnsupportedOperationException()
+}
diff --git a/src/es/akaya/src/eu/kanade/tachiyomi/extension/es/akaya/AkayaFilters.kt b/src/es/akaya/src/eu/kanade/tachiyomi/extension/es/akaya/AkayaFilters.kt
new file mode 100644
index 000000000..77d1e6555
--- /dev/null
+++ b/src/es/akaya/src/eu/kanade/tachiyomi/extension/es/akaya/AkayaFilters.kt
@@ -0,0 +1,48 @@
+package eu.kanade.tachiyomi.extension.es.akaya
+
+import eu.kanade.tachiyomi.source.model.Filter
+
+class Genre(name: String, val id: Int) : Filter.CheckBox(name)
+open class GenreList(genres: List<Genre>) : Filter.Group<Genre>("Géneros", genres)
+
+class GenreFilter : GenreList(
+    listOf(
+        Genre("Acción", 9),
+        Genre("Arte", 34),
+        Genre("Boylove (yaoi)", 18),
+        Genre("Comedia", 21),
+        Genre("Crimen", 25),
+        Genre("Distópico", 15),
+        Genre("Drama", 35),
+        Genre("Fantasía", 8),
+        Genre("Girllove (yuri)", 27),
+        Genre("Isekai", 19),
+        Genre("LGBT", 16),
+        Genre("Monstruos", 10),
+        Genre("NSFW", 17),
+        Genre("Psicológico", 26),
+        Genre("Romance", 24),
+        Genre("Sci Fi", 23),
+        Genre("Slice of life", 13),
+        Genre("Steampunk", 20),
+        Genre("Superhéroe", 11),
+        Genre("Supernatural", 22),
+        Genre("Suspenso", 14),
+        Genre("Thriller", 12),
+
+    ),
+)
+
+class OrderFilter() : UriPartFilter(
+    "Ordenar por",
+    arrayOf(
+        Pair("Populares", "genres"),
+        Pair("Recientes", "genres-bydate"),
+        Pair("Nombre", "genres-byname"),
+    ),
+)
+
+open class UriPartFilter(displayName: String, private val vals: Array<Pair<String, String>>) :
+    Filter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) {
+    fun toUriPart() = vals[state].second
+}