UC Wiki
Public API · v1

Read the source library, programmatically

Every book in the Lineage of Legends shelf AND every speech in the mirrored Sun Myung Moon + FFWPU archive is exposed over a small JSON API — 322 books and 19,245 speeches in one read-only surface. Free, no auth, rate-limited per IP. CORS is open so you can call from a browser too.

Rate limits

Per IP, fixed window per minute:

  • content bucket — 60 req/min. Covers /api/v1/books* and /api/v1/speeches* endpoints.
  • search bucket — 10 req/min. Covers /api/v1/search.

Every response carries X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers. When you exceed the bucket you get a 429 with a Retry-After header.

GET/api/v1/booksbucket: content

List every book in the library — slug, title, author, source code, page count, and cover image URL.

curl https://www.lineage-of-legends.com/api/v1/books

{
  "count": 322,
  "books": [
    {
      "slug": "divine-principle",
      "title": "Exposition of the Divine Principle",
      "author": "Hyo Won Eu (compiled), HSA-UWC",
      "source": "DP96",
      "pageCount": 295,
      "coverUrl": "/covers/divine-principle.jpg"
    },
    …
  ]
}
GET/api/v1/books/{slug}bucket: content

Get a single book's metadata plus its full chapter list. Each chapter carries the paragraph id of its first paragraph — pass that to the text endpoint to stream chapter contents.

slug*
The book slug, e.g. "divine-principle".
curl https://www.lineage-of-legends.com/api/v1/books/divine-principle

{
  "slug": "divine-principle",
  "title": "Exposition of the Divine Principle",
  "author": "Hyo Won Eu (compiled), HSA-UWC",
  "pageCount": 295,
  "paragraphCount": 8423,
  "chapters": [
    { "number": 1, "title": "The Principle of Creation", "page": 30, "firstParagraphId": "p-30-1" },
    …
  ],
  "textUrl": "/api/v1/books/divine-principle/text"
}
GET/api/v1/books/{slug}/textbucket: content

Stream paragraphs from a book. Pass `from` and/or `to` paragraph ids to slice; otherwise you get the first 200 paragraphs. Hard cap of 500 paragraphs per response when both bounds are set.

from
Inclusive start paragraph id (e.g. "p-30-1").
to
Inclusive end paragraph id. If omitted, returns 200 paragraphs from `from`.
curl 'https://www.lineage-of-legends.com/api/v1/books/divine-principle/text?from=p-30-1&to=p-45-2'

{
  "slug": "divine-principle",
  "from": "p-30-1",
  "to": "p-45-2",
  "count": 142,
  "totalCount": 8423,
  "truncated": false,
  "nextFrom": null,
  "paragraphs": [
    { "paraId": "p-30-1", "page": 30, "text": "Chapter 1" },
    …
  ]
}
GET/api/v1/speechesbucket: content

Paginated list across the 19,245-speech corpus. Filter by author (canonical display name), route_author (on-disk folder slug), year, decade, or free-text query. Meta only — fetch a single speech's text via the per-speech endpoint.

author
Canonical display_author, e.g. "Sun Myung Moon".
route_author
On-disk folder slug, e.g. "sunmyungmoon11".
year
Filter by date_guess starting with YYYY.
decade
Filter by decade, e.g. "1970s".
q
Case-insensitive substring on title + author.
limit
Per-page size, 1–200 (default 50).
offset
Pagination offset (default 0).
curl 'https://www.lineage-of-legends.com/api/v1/speeches?author=Sun%20Myung%20Moon&decade=1970s&limit=2'

{
  "count": 2,
  "total": 1284,
  "offset": 0,
  "limit": 2,
  "hasMore": true,
  "nextOffset": 2,
  "filters": { "author": "Sun Myung Moon", "decade": "1970s", … },
  "speeches": [
    {
      "slug": "the-way-our-family-must-go",
      "routeAuthor": "sunmyungmoon77",
      "author": "Sun Myung Moon",
      "rawAuthor": "SunMyungMoon77",
      "title": "The Way Our Family Must Go",
      "dateGuess": "1977-10-23",
      "sourceUrl": "https://tparents.org/…",
      "sourceKind": "htm",
      "wordCount": 4912,
      "textUrl": "/api/v1/speeches/sunmyungmoon77/the-way-our-family-must-go"
    },
    …
  ]
}
GET/api/v1/speeches/authorsbucket: content

Speaker index — one row per canonical display_author with the count of speeches in the corpus. Use it to discover speakers without paginating the full /speeches list.

curl https://www.lineage-of-legends.com/api/v1/speeches/authors

{
  "count": 412,
  "totalSpeeches": 19245,
  "authors": [
    { "author": "Sun Myung Moon",  "routeAuthor": "sunmyungmoon",  "count": 12871 },
    { "author": "Hak Ja Han",      "routeAuthor": "hakjahan",      "count": 1442 },
    …
  ]
}
GET/api/v1/speeches/{author}/{slug}bucket: content

Full transcript of one speech — metadata plus the polished paragraph array (logistics stripped, typography normalised). `author` is the on-disk folder slug (`routeAuthor` in the list response).

author*
On-disk folder slug, e.g. "sunmyungmoon77".
slug*
Speech slug.
curl https://www.lineage-of-legends.com/api/v1/speeches/sunmyungmoon77/the-way-our-family-must-go

{
  "slug": "the-way-our-family-must-go",
  "routeAuthor": "sunmyungmoon77",
  "author": "Sun Myung Moon",
  "rawAuthor": "SunMyungMoon77",
  "title": "The Way Our Family Must Go",
  "dateGuess": "1977-10-23",
  "sourceUrl": "https://tparents.org/…",
  "wordCount": 4912,
  "paragraphCount": 87,
  "paragraphs": [ "…", "…", … ],
  "anchorPrefix": "p-"
}
GET/api/v1/searchbucket: search

Search across the whole corpus. `results` are book-level hits (title, author, chapter heading); `passages` are body-text hits with a highlighted snippet and deep-link paragraph id. Multi-term queries require every term to match. The body-text index builds lazily on first call (~5-10s cold start) and is cached for the process lifetime — pass ?body=0 to skip passage search.

q*
Query string. 1–100 chars, up to 6 terms.
body
0 to skip body-text passage search and only return book-level matches. Default 1.
curl 'https://www.lineage-of-legends.com/api/v1/search?q=true+parents+heart'

{
  "query": "true parents heart",
  "count": 18,
  "results": [ /* book-level hits */ ],
  "passages": {
    "count": 24,
    "results": [
      {
        "slug": "cheon-seong-gyeong",
        "bookTitle": "Cheon Seong Gyeong",
        "paraId": "p-142-3",
        "page": 142,
        "snippet": "…the **heart** of **true parents** flows through every…",
        "score": 2
      },
      …
    ]
  }
}

Stability + versioning

This is v1. We won't make breaking changes to response shapes inside /api/v1/* — new fields may be added but existing ones won't be renamed or removed. Bigger changes ship under /api/v2.

Found a bug or want a new endpoint? Open an issue at github.com/hexgeta/uc-timeline.