Archiveofourown APIarchiveofourown.org ↗
Search AO3 works, retrieve chapter lists, and aggregate author statistics via 3 endpoints covering fandoms, ratings, kudos, word counts, and more.
What is the Archiveofourown API?
The AO3 API exposes 3 endpoints for querying publicly visible works on Archive of Our Own. The search_works endpoint mirrors AO3's native filter set — fandoms, ratings, kudos ranges, hit counts, free-text queries — and returns up to 20 results per page with full blurb metadata. Alongside it, get_author_stats aggregates per-author totals across works, and get_work_chapters returns the ordered chapter index for any single work.
curl -X GET 'https://api.parse.bot/scraper/44916edf-1e04-4103-ad4e-20228ad0396d/search_works?fandoms=Naruto&sort_by=kudos' \ -H 'X-API-Key: $PARSE_API_KEY'
Typed, relational, agent-ready
A generated client with real types, enums, and the links between objects — the structure a flat JSON response can't carry. Autocompletes in your editor and reads cleanly to coding agents.
- Fully typed · autocompletes
- Objects link to objects
- Typed errors & pagination
Typed Python client. Set up the SDK in your uv project, then pull this API’s typed client:
uv add parse-sdk uv run parse init uv run parse add --marketplace archiveofourown-org-api
uv run parse add --marketplace pulls a pinned snapshot of this canonical API — it won’t change underneath you. To customize it, subscribe and swap to your own copy.
"""Walkthrough: AO3 SDK — search works, browse chapters, check author stats."""
from parse_apis.archiveofourown_org_api import AO3, SortBy, InputNotFound
client = AO3()
# Search for top Naruto works by kudos, capped at 5 results.
for work in client.works.search(fandoms="Naruto", sort_by=SortBy.KUDOS, limit=5):
print(work.title, f"kudos={work.kudos}", f"hits={work.hits}")
# Drill into the first result's chapter list.
work = client.works.search(fandoms="Naruto", sort_by=SortBy.KUDOS, limit=1).first()
if work is not None:
for ch in work.chapters.list(limit=5):
print(f" Ch {ch.number}: {ch.title} ({ch.date_posted})")
# Look up the first author's aggregate stats.
author_username = work.authors[0].username
try:
stats = client.author_stats.get(author=author_username, max_pages=3)
print(f"{stats.username}: {stats.total_works} works, avg kudos {stats.average_kudos_per_work}")
for fandom in stats.fandoms[:3]:
print(f" {fandom.name}: {fandom.works} works")
except InputNotFound:
print(f"Author '{author_username}' not found")
print("exercised: works.search / chapters.list / author_stats.get")
Searches publicly visible AO3 works (works restricted to logged-in users are never included) using AO3's work-search filters and returns one page of 20 results with full blurb metadata per work: title, authors (with username/pseud), fandoms, rating, archive warnings, category, relationships, characters, freeform tags, summary, language, word count, chapter counts, completion flag, kudos, comments, bookmarks, hits, date updated, and direct download URLs for epub/mobi/pdf/html/azw3. Pagination is page-based: `page` defaults to 1, `total_pages` and `has_more` describe the upstream result stream. Numeric filters (word_count, hits, kudos, comments, bookmarks) accept AO3 range syntax: a number, '<N', '>N', or 'N-M'. When `include_author_stats` or `min_author_avg_kudos` is set, each work gains an `author_stats` array computed from each author's public works listing (up to `author_stats_pages` pages of 20 works per author, newest first; `is_complete` says whether all of the author's works were counted) — this costs one extra request per author page, so roughly up to 20 x author_stats_pages extra requests per call. `min_author_avg_kudos` drops works on the page whose authors all have average kudos per work below the threshold; it filters only the current page, so a filtered page may be empty while `has_more` is still true. Anonymous works have no author stats. A valid search with no matches returns an empty `works` list with total_results 0.
| Param | Type | Description |
|---|---|---|
| hits | string | Hits filter in AO3 range syntax (number, '<N', '>N', 'N-M'). |
| page | integer | 1-based results page; each page holds up to 20 works. |
| tags | string | Additional (freeform) tag name(s), comma-separated. |
| kudos | string | Kudos filter in AO3 range syntax (number, '<N', '>N', 'N-M'). |
| query | string | Free-text query over the work's text and metadata (AO3 'Any Field' search). |
| title | string | Words that must appear in the work title. |
| rating | string | Restrict to one AO3 rating. Omitted = any rating. |
| fandoms | string | Fandom tag name(s), comma-separated as on AO3 (e.g. Naruto). |
| sort_by | string | Sort column. Omitted = best match. |
| comments | string | Comments filter in AO3 range syntax (number, '<N', '>N', 'N-M'). |
| creators | string | Author/artist name to match (AO3 creators filter). |
| language | string | AO3 language code as used in the site's language dropdown (e.g. en, es, zh, ru). Omitted = any language. |
| warnings | string | Comma-separated archive warning codes from the Warning enum (e.g. none_apply,violence). Omitted = no warning filter. |
| bookmarks | string | Bookmarks filter in AO3 range syntax (number, '<N', '>N', 'N-M'). |
| categories | string | Comma-separated relationship category codes from the Category enum (e.g. m_m,gen). Omitted = any category. |
| characters | string | Character tag name(s), comma-separated. |
| completion | string | Completion status filter. Omitted = all works. |
| crossovers | string | Crossover handling. Omitted = include crossovers. |
| word_count | string | Word count filter in AO3 range syntax: '1000', '<5000', '>50000', or '10000-20000'. |
| date_updated | string | Date-updated filter in AO3 syntax: a date 'YYYY-MM-DD', or a relative range such as '< 7 days', '> 2 years ago', '1-3 months'. |
| relationships | string | Relationship tag name(s), comma-separated, exactly as tagged on AO3. |
| single_chapter | boolean | true restricts results to single-chapter works. |
| sort_direction | string | Sort direction. Omitted = AO3's default for the chosen column (descending for counts and dates). |
| author_stats_pages | integer | How many 20-work pages of each author's works listing to aggregate when author stats are requested; clamped to 3. |
| include_author_stats | boolean | true attaches `author_stats` (aggregates from each author's public works listing) to every work on the page. Adds one request per author page. |
| min_author_avg_kudos | number | Keep only works on this page where at least one author has average_kudos_per_work >= this value. Implies include_author_stats. |
{
"type": "object",
"fields": {
"page": "integer current page",
"works": "array of work records: work_id (string), title, url, authors[] {name, username, pseud}, fandoms[], rating, warnings[], warning_symbol, category, relationships[], characters[], tags[], summary, language, language_code, word_count, chapters_posted, chapters_total (null when unknown '?'), is_complete, comments, kudos, bookmarks, hits, date_updated ('DD Mon YYYY'), downloads {epub,mobi,pdf,html,azw3 URLs}, author_stats[] (only when author stats were requested; same shape as get_author_stats minus the works list)",
"has_more": "boolean, true when a later page exists",
"total_pages": "integer last page number in the result stream",
"site_messages": "array of notice/error strings AO3 printed on the results page",
"total_results": "integer total matching works reported by AO3",
"results_on_page": "integer works returned after any author filter",
"min_author_avg_kudos": "number threshold applied, or null (only when author stats were requested)",
"author_stats_unavailable": "array of usernames whose works listing could not be fetched (only when author stats were requested)"
},
"sample": {
"data": {
"page": 2,
"works": [
{
"url": "https://archiveofourown.org/works/14035764",
"hits": 407485,
"tags": [
"Time Travel",
"Humor"
],
"kudos": 18909,
"title": "Sasuke's No Good Very Bad Teammates",
"rating": "Teen And Up Audiences",
"authors": [
{
"name": "GwendolynStacy",
"pseud": "GwendolynStacy",
"username": "GwendolynStacy"
}
],
"fandoms": [
"Naruto"
],
"summary": "Naruto and Sakura have gone insane.",
"work_id": "14035764",
"category": "Gen",
"comments": 3636,
"language": "English",
"warnings": [
"No Archive Warnings Apply"
],
"bookmarks": 5749,
"downloads": {
"pdf": "https://archiveofourown.org/downloads/14035764/Sasuke_s_No_Good_Very_Ba.pdf?updated_at=1785690977",
"azw3": "https://archiveofourown.org/downloads/14035764/Sasuke_s_No_Good_Very_Ba.azw3?updated_at=1785690977",
"epub": "https://archiveofourown.org/downloads/14035764/Sasuke_s_No_Good_Very_Ba.epub?updated_at=1785690977",
"html": "https://archiveofourown.org/downloads/14035764/Sasuke_s_No_Good_Very_Ba.html?updated_at=1785690977",
"mobi": "https://archiveofourown.org/downloads/14035764/Sasuke_s_No_Good_Very_Ba.mobi?updated_at=1785690977"
},
"characters": [
"Uchiha Sasuke",
"Uzumaki Naruto"
],
"word_count": 72633,
"is_complete": true,
"author_stats": [
{
"fandoms": [
{
"name": "The Avengers (Marvel Movies)",
"works": 6
}
],
"username": "GwendolynStacy",
"total_hits": 1847350,
"is_complete": false,
"total_kudos": 95138,
"total_pages": 2,
"total_works": 32,
"pages_fetched": 1,
"works_counted": 20,
"complete_works": 19,
"total_comments": 18063,
"total_bookmarks": 26550,
"total_word_count": 671722,
"average_word_count": 33586.1,
"average_hits_per_work": 92367.5,
"average_kudos_per_work": 4756.9,
"average_bookmarks_per_work": 1327.5
}
],
"date_updated": "20 Jun 2020",
"language_code": "en",
"relationships": [
"Haruno Sakura & Uchiha Sasuke & Uzumaki Naruto"
],
"chapters_total": 32,
"warning_symbol": "No Archive Warnings Apply",
"chapters_posted": 32
}
],
"has_more": true,
"total_pages": 4636,
"site_messages": [],
"total_results": 92708,
"results_on_page": 20,
"min_author_avg_kudos": 3000,
"author_stats_unavailable": []
},
"status": "success"
}
}About the Archiveofourown API
Searching Works
search_works accepts the same filters available on AO3's work search page: fandoms (comma-separated tag names), rating, tags for additional freeform tags, title, and a free-text query covering both text and metadata. Numeric filters — kudos and hits — accept AO3's own range syntax: a plain number for exact match, <N, >N, or N-M for ranges. Results come back paginated at 20 works per page; total_results, total_pages, and has_more let you walk the full result stream. Each work record includes work_id, url, authors (with username, pseud, and display name), fandoms, rating, warnings, and warning_sym. The site_messages field surfaces any notice or error strings AO3 itself printed alongside the results.
Author Statistics
get_author_stats takes an AO3 username and walks that user's public works listing, aggregating total_kudos, total_hits, word counts, bookmarks, and comments across the works it counts. The max_pages parameter (capped at 25) bounds how many listing pages — 20 works each — are fetched; is_complete tells you whether the aggregation covered every public work or was capped early. The fandoms array in the response ranks fandoms by frequency across the counted works. Individual work records in the works array include is_complete, date_updated, and per-engagement counts, making the endpoint useful for building author-level profiles. You can pass author stats inline with search_works results to filter by min_author_avg_kudos.
Chapter Index
get_work_chapters accepts a work_id (the numeric ID returned by search_works) and returns the full chapter index: ordered chapters array with chapter_id, number, title, url, and date_posted (YYYY-MM-DD format), plus the total chapter_count and the work's first-listed author. Works visible only to logged-in AO3 users are not accessible; attempting to retrieve one returns an input error rather than partial data.
The Archiveofourown API is a managed, monitored endpoint for archiveofourown.org — not a raw scraper you maintain. Every endpoint is automatically health-checked on a schedule, and when archiveofourown.org changes and a check fails, the API is automatically queued for repair and re-verified. It is built to keep working as the site underneath it changes.
This isn't an official archiveofourown.org API — it's an independent, maintained REST wrapper over public data. Where the source has no official API (or only a limited one), Parse gives you a stable contract over a source that never promised one, and keeps it current. Need a new endpoint or field? You can revise it yourself in plain English and the agent rebuilds it against the live site in minutes — contributing the change back to the shared API is free.
Will this API break when the source site changes?+
Is this an official API from the source site?+
Can I fix or extend this API myself if I need a new endpoint or field?+
What happens if I call an endpoint that has an issue?+
- Build a fandom-specific reading list tool filtered by
rating,kudosrange, and fandom tag name. - Track a fan author's output over time using
total_works,total_kudos, anddate_updatedfromget_author_stats. - Power a chapter-release monitor by polling
get_work_chaptersand comparingchapter_countbetween runs. - Identify prolific authors in a fandom by joining
search_worksresults with aggregatedget_author_statskudos totals. - Analyze fandom distribution for a given author using the ranked
fandomsarray fromget_author_stats. - Build a recommendation feed for works above a kudos threshold using the
>Nrange syntax on thekudosparameter. - Export chapter metadata for archival or reading-app import using
chapter_id,url, anddate_postedfields.
| Tier | Price | Credits/month | Rate limit |
|---|---|---|---|
| Free | $0/mo | 200 | 5 req/min |
| Hobby | $30/mo | 1,000 | 20 req/min |
| Developer | $100/mo | 5,000 | 100 req/min |
| Team | $300/mo | 20,000 | 300 req/min |
| Company | $1,000/mo | 100,000 | 500 req/min |
Each endpoint has a fixed posted price per successful call — most fall between 1 and 10 credits — shown on this API's page before you run it. Exceeding the rate limit returns a 429 response. Authenticate with the X-API-Key header.
Does Archive of Our Own have an official developer API?+
Does `search_works` return works that require an AO3 login to view?+
search_works and get_work_chapters. get_author_stats also only counts public works from the logged-out listing view.How does pagination work across `search_works` results?+
page (1-based). total_results and total_pages tell you the full scope of the result set, and has_more is true when additional pages exist. AO3 itself caps browsable results at a few thousand entries for large queries, so total_results may exceed what is actually pageable.Can I retrieve the full text of a work or individual chapter content?+
get_work_chapters, and work-level metadata via search_works, but does not return chapter body text. You can fork this API on Parse and revise it to add an endpoint that fetches chapter content.Does `get_author_stats` cover a user's bookmarks or reading history?+
get_author_stats aggregates over works the user has posted — kudos, hits, bookmarks received, comments, word count — not the user's own bookmarks list or reading history. You can fork this API on Parse and revise it to add a bookmarks endpoint.