DaoSearch
API
A read-only JSON API over the same data as the site: books, search, catalogues, rankings and booklists from six Chinese web-novel sources. Free, with a key from your account.
What it is for
The API gives a program the same things the site shows a reader: a book and its figures, search, each source's catalogue with its filters, the ranking boards and the booklists. It is read-only JSON over GET, and it adds nothing the pages do not have.
It is meant for personal projects and tools: a Discord bot, a userscript, a reading tracker, an agent that looks a novel up. It is not a way to copy the catalogue.
Everything lives under https://daosearch.io/api/v1. A machine-readable description of every endpoint, parameter and field is at /api/v1/openapi.json (OpenAPI 3.1, no key needed), and it is generated from the same code that answers the requests.
Getting a key
Every request needs a key. Sign in, open your account page, and create one under "API keys".
- The key is shown once, when you create it. Store it then: only a hash of it is kept, so nobody can show it to you again.
- An account can hold 3 keys, and it has to be a day old before it can create its first.
- "Rotate" gives a key a new secret and keeps the old one working for 24 hours, so you can switch a running tool over.
- "Revoke" stops a key within a minute.
We keep the day a key was last used and today's count against the limits. We do not keep what a key asked for.
Authentication
Send the key in the Authorization header.
curl -H "Authorization: Bearer $DAOSEARCH_KEY" \
-H "User-Agent: my-tool/1.0" \
"https://daosearch.io/api/v1/books/qidian/1010868264"
A key is never accepted in the URL: a request with ?key= or ?api_key= is answered 400, because URLs end up in logs. The API cannot be called from a web page on another site either, since a key in a page is a published key. Call it from a server, a script, a browser extension or a userscript.
Limits
- 60 requests a minute for each key. Past it the answer is 429
rate_limited, andRetry-Aftersays how many seconds are left in the minute. - 5,000 units a day for each account, shared by all its keys, starting again at 00:00 UTC. Past it the answer is 429
quota_exceeded, andRetry-Afteris the seconds until midnight UTC.
A refused request costs nothing. Every answer carries three headers about the daily units, so a tool can pace itself without a failed call:
| header | meaning |
|---|---|
RateLimit-Limit |
the account's units for a day |
RateLimit-Remaining |
units left today, after this request |
RateLimit-Reset |
seconds until the units are given back |
Answers come with Cache-Control: private, max-age=300: keep what you fetch for at least five minutes. The data behind an answer is refreshed less often than that anyway (the table below says how often), so asking again sooner returns the same thing.
What each call costs
A single record costs 1 unit. A page of a listing, which is up to 50 books or lists, costs 5.
| call | units | refreshed |
|---|---|---|
GET /sources |
1 | every 9 hours |
GET /books/{source}/{source_book_id} |
1 | every 6 hours |
GET /{source}/rankings |
1 | every 30 minutes |
GET /{source}/rankings/{board} |
5 | every 30 minutes |
GET /{source}/booklists |
5 | every hour |
GET /{source}/booklists/{list_id} |
5 | every hour |
GET /search |
1 | every 6 hours |
GET /{source}/genres |
1 | every 9 hours |
GET /{source}/tags |
1 | every 9 hours |
GET /{source}/books |
5 | every 15 minutes |
How answers are shaped
- A data answer is
{"data": ...}. A listing adds"page". An error is{"error": {...}}. Nothing else sits at the top level. - A book is named by its
sourceand itssource_book_id: the id in the source's own URL, sent as text because Fanqie's are 19 digits. The sources areqidian,fanqie,jjwxc,faloo,qimaoandzongheng. - Text in two languages is
{"en": ..., "zh": ...}.enis null until it is translated. Genres and tags are named by theirzhin parameters. - A missing fact is
null, never0or an empty string. An empty list is[]. - Numbers are raw (
22256029, not22.3M). Times are RFC 3339 in UTC. - Every object carries
url, its page on DaoSearch. - A listing is paged with
page, 50 to a page.page.lastis the deepest page the view allows andpage.next_urlis the next request, null on the last page. view=compacton a listing drops each book's cover, synopsis excerpt and the labels of its figures: about a third of the size, for a tool that only needs to choose.- A parameter the endpoint does not have is refused with 400, not ignored. So is a value off its list.
The six sources count different things, so a book's figures come as a metrics array, each with a key, the label the site prints, a value and the period it is counted over. GET /sources describes every key a source can send.
Sources
GET /sources is the place to start. It lists the six sources and, for each, what every metric key means, which sorts and filters its catalogue takes and on which steps, and what its booklists accept. Read the allowed values from here and do not hard-code them. It changes rarely, so cache it for a day.
A metric with on_book: true is on a book's own record. One with on_book: false appears only on catalogue rows or on a ranking board's entries, such as a board's own weekly count.
curl -H "Authorization: Bearer $DAOSEARCH_KEY" \
"https://daosearch.io/api/v1/sources"
One source, cut to two of its figures:
{
"data": [
{
"source": "fanqie",
"label": "Fanqie",
"name_zh": "番茄小说",
"site_url": "https://fanqienovel.com",
"url": "https://daosearch.io/fanqie/library",
"guide_url": "https://daosearch.io/blog/fanqie-reads-vs-all-time-reads",
"has_rankings": true,
"has_booklists": true,
"score_scale": 10,
"metrics": [
{
"key": "reads",
"label": "Reads",
"zh": "在读",
"period": "current",
"on_book": true,
"meaning": "The read count Fanqie's app shows for the book now.",
"sort": "reads",
"min_param": null,
"min_steps": null
},
{
"key": "score",
"label": "Score",
"zh": "评分",
"period": null,
"on_book": true,
"meaning": "Reader score out of 10. A third of scored books sit on a 5.8 placeholder.",
"sort": "score",
"min_param": "min_score",
"min_steps": [
7,
8.5,
9,
9.5
]
}
],
"catalogue": {
"sorts": [
"reads",
"reads_all",
"updated",
"added",
"score",
"shelves",
"words"
],
"default_sort": "reads",
"length_steps": [
100000,
300000,
1000000,
2000000,
3000000
],
"facets": [
{
"param": "audience",
"values": [
"male",
"female"
]
}
]
},
"booklists": {
"sorts": [
"visits",
"books",
"recs",
"active"
],
"default_sort": "active",
"min_books_steps": [
50,
100,
200,
300,
500
],
"popularity": {
"param": "min_visits",
"steps": [
100000,
1000000,
10000000,
30000000
]
},
"member_sorts": [
"reads",
"score",
"shelves",
"words",
"updated"
],
"min_words_steps": [
100000,
300000,
1000000,
2000000,
3000000
],
"metrics": [
{
"key": "visits",
"label": "Views",
"meaning": "Visits to the list over its life. Fanqie publishes no follower count."
},
{
"key": "recommendations",
"label": "Recs",
"meaning": "Recommendations readers left on the list."
}
]
}
}
]
}
A book
GET /books/{source}/{source_book_id} is everything the book's page shows: titles and author in both languages, the whole English synopsis, genre, tags, the source's figures, the boards it ranks on now, whether the source still carries it, the same novel on other sources, and a link to an English translation when one is known.
- The Chinese synopsis is never sent. Titles and tags in Chinese are there because they identify the book.
availability.stateisavailable,removed_at_source(the source says the book is gone) orwithheld_at_source(the source has it but is not showing it now, which on JJWXC often passes).chapters_unavailableis reserved. Expect new values.- An id whose book is filed under another source answers 301 to that book's API URL.
- A book DaoSearch has hidden answers 404, as its page does.
curl -H "Authorization: Bearer $DAOSEARCH_KEY" \
"https://daosearch.io/api/v1/books/qidian/1010868264"
{
"data": {
"source": "qidian",
"source_book_id": "1010868264",
"url": "https://daosearch.io/qidian/book/1010868264/lord-of-the-mysteries",
"source_url": "https://www.qidian.com/book/1010868264/",
"title": {
"en": "Lord of the Mysteries",
"zh": "诡秘之主"
},
"author": {
"en": "Cuttlefish That Loves Diving",
"zh": "爱潜水的乌贼"
},
"cover_url": "https://daosearch.io/img/cover/qidian/1010868264.jpg",
"synopsis_en": "With the rising tide of steam power and machinery, who can come close to being a Beyonder?\n\nShrouded in the fog of history and darkness, who or what is the lurking evil that murmurs into our ears?",
"genre": {
"en": "Fantasy",
"zh": "玄幻"
},
"subgenre": {
"en": "Otherworld Continent",
"zh": "异世大陆"
},
"tags": [
{
"en": "Steampunk",
"zh": "蒸汽朋克"
},
{
"en": "Transmigration",
"zh": "穿越"
}
],
"facets": [
{
"key": "audience",
"label": "Audience",
"value": "male",
"value_label": "Male"
}
],
"status": "completed",
"word_count": 4465200,
"chapter_count": 1432,
"updated_at": "2020-05-01T12:00:00Z",
"refreshed_at": "2026-10-06T03:41:12Z",
"metrics": [
{
"key": "monthly_votes",
"label": "Votes",
"value": 15864,
"period": "month"
},
{
"key": "recommendation_votes",
"label": "Recs",
"value": 10203441,
"period": "all_time"
},
{
"key": "fans",
"label": "Fans",
"value": 428117,
"period": "all_time"
},
{
"key": "collections",
"label": "Collections",
"value": 6120344,
"period": "all_time"
}
],
"rankings": [
{
"board": "yuepiao",
"board_label": "Monthly Votes",
"channel": "overall",
"channel_label": null,
"position": 212,
"url": "https://daosearch.io/qidian/rankings?page=5"
}
],
"booklist_count": 5997,
"availability": {
"state": "available",
"since": null
},
"also_on": [
{
"source": "qimao",
"source_book_id": "1693271",
"url": "https://daosearch.io/qimao/book/1693271/lord-of-the-mysteries"
}
],
"english_translation": {
"site": "RandomTranslator",
"url": "https://randomtranslator.com/novel/4812"
},
"translated_by": "GPT-6 Luna"
}
}
Search
GET /search takes exactly one of two parameters.
qis a title in English or Chinese, or a book's URL at its source. A title returns up to 5 books across all sources, in the site's own order, and forgives a typo when nothing matches exactly. One Chinese character is enough; a query in Latin letters needs 3. A URL returns that one book, or nothing if we do not hold it.authoris an author's name: the first 5 books by that author on each source, so up to 30. For more of one author use the catalogue'sauthorfilter.sourcenarrows either to one source.
Sending both q and author, or neither, is a 400. There is no paging and no relevance score. A result is a short card: get the book for its details.
curl -H "Authorization: Bearer $DAOSEARCH_KEY" \
"https://daosearch.io/api/v1/search?q=lord+of+the+mysteries"
{
"data": [
{
"source": "qidian",
"source_book_id": "1010868264",
"url": "https://daosearch.io/qidian/book/1010868264/lord-of-the-mysteries",
"title": {
"en": "Lord of the Mysteries",
"zh": "诡秘之主"
},
"author": {
"en": "Cuttlefish That Loves Diving",
"zh": "爱潜水的乌贼"
},
"cover_url": "https://daosearch.io/img/cover/qidian/1010868264.jpg",
"matched": "title"
}
]
}
The catalogue
GET /{source}/books is a source's catalogue with the filters of its library page: title, author, status, genre, subgenre, tag and tag_not (both repeatable), tag_match, min_words, max_words, updated, dropped, sort, order, and each source's own filters and minimums.
- Sorts, the source's own filters (
audience, orpovandaccesson JJWXC) and the minimums on its figures differ by source.GET /sourceslists them, and the OpenAPI document repeats them per source. - A minimum such as
min_fansormin_scoretakes one of the source's steps, not any number. On Fanqie,min_readsfilters on lifetime reads (reads_all_time), not on the currentreadsfigure. genreandtagtake thezhof a genre or tag.subgenreneeds itsgenre.- A view with no filter can be paged 50 deep, a view with one filter 20, and a view with two or more 5. Each tag counts as a filter. Narrow the view to see more of it.
titletakes a piece of a title. A book URL there is a 400 that points you to/search.- There is no catalogue across sources and no sort across them: their figures do not compare.
curl -H "Authorization: Bearer $DAOSEARCH_KEY" \
"https://daosearch.io/api/v1/qidian/books?status=completed&min_fans=100000"
{
"data": [
{
"source": "qidian",
"source_book_id": "1010868264",
"url": "https://daosearch.io/qidian/book/1010868264/lord-of-the-mysteries",
"title": {
"en": "Lord of the Mysteries",
"zh": "诡秘之主"
},
"author": {
"en": "Cuttlefish That Loves Diving",
"zh": "爱潜水的乌贼"
},
"cover_url": "https://daosearch.io/img/cover/qidian/1010868264.jpg",
"genre": {
"en": "Fantasy",
"zh": "玄幻"
},
"status": "completed",
"word_count": 4465200,
"synopsis_excerpt": "With the rising tide of steam power and machinery, who can come close to being a Beyonder? Shrouded in the fog of history and darkness, who or what is the lurking evil that murmurs into our ears?",
"metrics": [
{
"key": "monthly_votes",
"label": "Votes",
"value": 15864,
"period": "month"
},
{
"key": "recommendation_votes",
"label": "Recs",
"value": 10203441,
"period": "all_time"
},
{
"key": "fans",
"label": "Fans",
"value": 428117,
"period": "all_time"
}
]
}
],
"page": {
"number": 1,
"size": 50,
"count": 50,
"total": 213,
"total_capped": false,
"last": 5,
"next_url": "https://daosearch.io/api/v1/qidian/books?min_fans=100000&page=2&status=completed"
}
}
Genres and tags
GET /{source}/genres lists a source's genres with their subgenres, and GET /{source}/tags its tags with how many books carry each. These are the values the catalogue's genre, subgenre, tag and tag_not take. book_count on a genre is null when it holds fewer than 25 books. The tags are every tag on 25 or more of the source's books, up to 3,000 in one answer; q narrows them by name.
curl -H "Authorization: Bearer $DAOSEARCH_KEY" \
"https://daosearch.io/api/v1/qidian/genres"
{
"data": [
{
"zh": "玄幻",
"en": "Fantasy",
"book_count": 214530,
"url": "https://daosearch.io/qidian/library?genre=3",
"audience": "male",
"subgenres": [
{
"zh": "东方玄幻",
"en": "Eastern Fantasy"
},
{
"zh": "异世大陆",
"en": "Otherworld Continent"
}
]
}
]
}
curl -H "Authorization: Bearer $DAOSEARCH_KEY" \
"https://daosearch.io/api/v1/qidian/tags?q=system"
{
"data": [
{
"zh": "系统",
"en": "System",
"book_count": 88214,
"url": "https://daosearch.io/qidian/library?tag_in=412"
}
]
}
Rankings
GET /{source}/rankings lists the boards we hold for a source now: each board's code, its name, what it ranks by, how deep it goes and its channels (a genre, an audience, or the whole board). Boards come and go, so list them and do not hard-code a code.
GET /{source}/rankings/{board} is one page of a board as the source last published it. Each entry has its position, its previous_position at the last daily snapshot, and movement: new, up, down or flat. channel picks one of the board's channels.
depthis how many places a board can hold: 100 on most, 200 on Zongheng, 500 on Qidian's monthly votes.page.totalis how many the channel holds now.- Only places the source itself published are returned. On the site, Qidian's genre boards for monthly votes go on past Qidian's own last place using the votes we hold; those continued places are not in the API.
ranked_bynames the metric key the board is ordered by, and that figure is first in each book'smetricswith"ranked_by": true. It is null where the source publishes no figure for the board. Some boards carry a figure of their own, such as Faloo'sweekly_readsor Qimao'sheat:GET /sourcesdescribes each.- A book first seen on a board has a null title until we have indexed it.
- An unknown board or channel is a 404. We do not answer with a different board.
curl -H "Authorization: Bearer $DAOSEARCH_KEY" \
"https://daosearch.io/api/v1/qidian/rankings"
{
"data": [
{
"board": "yuepiao",
"label": "Monthly Votes",
"zh": "月票榜",
"description": "Qidian's monthly vote ranking: readers spend monthly tickets on the books they follow.",
"ranked_by": "monthly_votes",
"depth": 500,
"default_channel": "overall",
"channels": [
{
"channel": "overall",
"label": "All",
"zh": "全部"
},
{
"channel": "21",
"label": "Fantasy",
"zh": "玄幻"
}
],
"url": "https://daosearch.io/qidian/rankings"
}
]
}
curl -H "Authorization: Bearer $DAOSEARCH_KEY" \
"https://daosearch.io/api/v1/qidian/rankings/yuepiao?view=compact"
This one is the compact view:
{
"data": {
"source": "qidian",
"board": "yuepiao",
"label": "Monthly Votes",
"zh": "月票榜",
"channel": "overall",
"channel_label": "All",
"ranked_by": "monthly_votes",
"refreshed_at": "2026-10-08T02:10:44Z",
"url": "https://daosearch.io/qidian/rankings",
"entries": [
{
"position": 1,
"previous_position": 3,
"movement": "up",
"change": 2,
"book": {
"source": "qidian",
"source_book_id": "1010868264",
"url": "https://daosearch.io/qidian/book/1010868264/lord-of-the-mysteries",
"title": {
"en": "Lord of the Mysteries",
"zh": "诡秘之主"
},
"author": {
"en": "Cuttlefish That Loves Diving",
"zh": "爱潜水的乌贼"
},
"genre": {
"en": "Fantasy",
"zh": "玄幻"
},
"status": "completed",
"word_count": 4465200,
"metrics": [
{
"key": "monthly_votes",
"value": 15864,
"ranked_by": true
},
{
"key": "recommendation_votes",
"value": 10203441
},
{
"key": "fans",
"value": 428117
}
]
}
}
]
},
"page": {
"number": 1,
"size": 50,
"count": 50,
"total": 500,
"total_capped": false,
"last": 10,
"next_url": "https://daosearch.io/api/v1/qidian/rankings/yuepiao?page=2&view=compact"
}
}
Booklists
GET /{source}/booklists lists the reader-made lists of a source, and GET /{source}/booklists/{list_id} is one list with a page of its books. Zongheng has no booklists: both answer 404 not_supported there, and has_booklists in /sources says so in advance.
- A list is named by its id at the source (
list_id). - On the index,
sort,min_booksand the source's popularity minimum take the values in/sources.contains=qidian:1010868264finds the lists that hold a book. - On one list,
sortreorders its books by one of the source's figures, andstatus,min_words,droppedandhas_notefilter them.min_wordstakes one of the source's steps (booklists.min_words_stepsin/sources).has_noteexists only where lists carry curators' notes. - A list of more than 5,000 books cannot be sorted or filtered: that is a 400
sort_not_available. Ask for it plain. - A book on a list can belong to another source than the list. Each book says its own
source.
curl -H "Authorization: Bearer $DAOSEARCH_KEY" \
"https://daosearch.io/api/v1/qidian/booklists?min_books=30"
{
"data": [
{
"source": "qidian",
"list_id": "628204115",
"url": "https://daosearch.io/qidian/booklists/628204115/apocalypse-survival-picks",
"title": {
"en": "Apocalypse Survival Picks",
"zh": "末世求生精选"
},
"description_en": "Thirty survival stories that keep their rules straight.",
"book_count": 30,
"metrics": [
{
"key": "followers",
"label": "Follows",
"value": 1840
}
],
"updated_at": "2026-09-29T08:00:00Z"
}
],
"page": {
"number": 1,
"size": 50,
"count": 50,
"total": 2500,
"total_capped": true,
"last": 20,
"next_url": "https://daosearch.io/api/v1/qidian/booklists?min_books=30&page=2"
}
}
curl -H "Authorization: Bearer $DAOSEARCH_KEY" \
"https://daosearch.io/api/v1/qidian/booklists/628204115"
{
"data": {
"source": "qidian",
"list_id": "628204115",
"url": "https://daosearch.io/qidian/booklists/628204115/apocalypse-survival-picks",
"title": {
"en": "Apocalypse Survival Picks",
"zh": "末世求生精选"
},
"description_en": "Thirty survival stories that keep their rules straight.",
"book_count": 30,
"metrics": [
{
"key": "followers",
"label": "Follows",
"value": 1840
}
],
"updated_at": "2026-09-29T08:00:00Z",
"translated_by": "GPT-6 Luna",
"members": [
{
"position": 1,
"note": "The best rule system of the genre, and it never cheats.",
"book": {
"source": "qidian",
"source_book_id": "1010868264",
"url": "https://daosearch.io/qidian/book/1010868264/lord-of-the-mysteries",
"title": {
"en": "Lord of the Mysteries",
"zh": "诡秘之主"
},
"author": {
"en": "Cuttlefish That Loves Diving",
"zh": "爱潜水的乌贼"
},
"cover_url": "https://daosearch.io/img/cover/qidian/1010868264.jpg",
"genre": {
"en": "Fantasy",
"zh": "玄幻"
},
"status": "completed",
"word_count": 4465200,
"synopsis_excerpt": "With the rising tide of steam power and machinery, who can come close to being a Beyonder? Shrouded in the fog of history and darkness, who or what is the lurking evil that murmurs into our ears?",
"metrics": [
{
"key": "monthly_votes",
"label": "Votes",
"value": 15864,
"period": "month"
},
{
"key": "recommendation_votes",
"label": "Recs",
"value": 10203441,
"period": "all_time"
},
{
"key": "fans",
"label": "Fans",
"value": 428117,
"period": "all_time"
}
]
}
}
]
},
"page": {
"number": 1,
"size": 50,
"count": 30,
"total": 30,
"total_capped": false,
"last": 1,
"next_url": null
}
}
Errors
An error is JSON with a stable code, the HTTP status, a message that says what to do next, and param when one parameter is at fault. Branch on code. Messages may be reworded.
{
"error": {
"code": "invalid_parameter",
"status": 400,
"param": "min_score",
"message": "min_score must be one of 7, 8.5, 9, 9.5 on fanqie.",
"docs_url": "https://daosearch.io/developers#errors"
}
}
| status | code | when |
|---|---|---|
| 400 | invalid_parameter |
A parameter's value is not one the endpoint takes. param names it and the message lists what it does take. Also answered for a key sent in the URL. |
| 400 | unknown_parameter |
The request sent a parameter the endpoint does not have. The message lists the ones it has. |
| 400 | page_out_of_range |
page is past the last page this view allows. The message names the last page. |
| 400 | sort_not_available |
A booklist too large to sort was asked for sorted or filtered. Ask for it without sort and filters. |
| 401 | missing_key |
No Authorization header, or not a Bearer one. |
| 401 | invalid_key |
The key is not one DaoSearch issued, or it has expired after a rotation. |
| 401 | revoked_key |
The key was revoked. Create a new one on your account page. |
| 403 | account_blocked |
API access for the key's account was blocked by DaoSearch. |
| 403 | cors_not_allowed |
A browser asked, for a web page on another origin, whether it may call the API. It may not. |
| 404 | not_found |
No such book, board, channel, list or path. A book DaoSearch has hidden is not_found too. |
| 404 | unknown_source |
The {source} segment is not one of the sources. The message lists them. |
| 404 | not_supported |
The source has no such resource: Zongheng has no booklists. |
| 405 | method_not_allowed |
The API is read-only: GET and HEAD. |
| 429 | rate_limited |
The key made more requests this minute than it may. Wait Retry-After seconds. |
| 429 | quota_exceeded |
The account has used its units for today. Retry-After is the seconds until 00:00 UTC. |
| 500 | internal |
Something failed on our side. Try again in a moment. |
| 503 | api_disabled |
The API is switched off for now. Try again later. |
A 401 also carries WWW-Authenticate: Bearer. A 429 and a 503 carry Retry-After, in seconds.
Terms
By creating a key you agree to these.
- The API is free, for personal projects and tools. It may change or stop; a breaking change gets a new version path and 90 days.
- Show a link to the book's url wherever you show its data.
- Keep to the limits and cache what you fetch. The RateLimit headers tell you where you stand.
- Do not use it to copy the catalogue or to train on it in bulk. Titles, synopses and covers belong to their authors and the source sites.
- Keep the key out of public pages and repositories. One you publish will be revoked.
- Send a User-Agent that names your tool.
DaoSearch provides translations and links for discovery. The works belong to their authors and to the sites that publish them.
Versioning
/api/v1 is the contract. These are not a new version, so write a client that tolerates them: a new field, a new endpoint, a new optional parameter, a new value in a list such as availability.state, a new metric key, a looser limit.
Removing or renaming a field, changing what one means, or making a parameter required would be a new path, /api/v2. /api/v1 would then keep working for at least 90 days and say when it ends in a Sunset header.
The limits and these terms are not part of the version. They may tighten, and this page will say so first.
For LLM tools
The OpenAPI document gives each endpoint an operationId that reads as an intent (get_book, search_books, list_catalogue), a description that says when to use it and what it costs, and an example. Most agent tooling can turn it into tools directly. /llms.txt describes the site's public pages in a few lines.
Contact
Questions, a bug, or a limit that is too tight for something reasonable: write to [email protected] or ask in the Discord. If a key has leaked, revoke it on your account page first.