Install
openclaw skills install apple-booksRead your Apple Books library, highlights, notes, and reading progress directly from the local SQLite databases on macOS.
openclaw skills install apple-booksQuery your local Apple Books library on macOS. Read-only access to books, highlights, notes, collections, and reading progress.
~/Library/Containers/com.apple.iBooksX/The database filenames are consistent across macOS installs, but always resolve them dynamically in case Apple changes them in future versions:
BKLIBRARY_DB="$(ls ~/Library/Containers/com.apple.iBooksX/Data/Documents/BKLibrary/*.sqlite 2>/dev/null | head -1)"
AEANNOTATION_DB="$(ls ~/Library/Containers/com.apple.iBooksX/Data/Documents/AEAnnotation/*.sqlite 2>/dev/null | head -1)"
If either variable is empty, Apple Books has not been set up on this Mac.
Important: These are read-only queries. Never INSERT, UPDATE, or DELETE rows — doing so may corrupt Apple Books data or cause iCloud sync issues.
BKLIBRARY_DB="$(ls ~/Library/Containers/com.apple.iBooksX/Data/Documents/BKLibrary/*.sqlite 2>/dev/null | head -1)"
sqlite3 "$BKLIBRARY_DB" \
"SELECT ZTITLE, ZAUTHOR, ZGENRE, ZPAGECOUNT, ZREADINGPROGRESS, ZISFINISHED, ZASSETID
FROM ZBKLIBRARYASSET
WHERE ZTITLE IS NOT NULL
ORDER BY ZLASTOPENDATE DESC;"
BKLIBRARY_DB="$(ls ~/Library/Containers/com.apple.iBooksX/Data/Documents/BKLibrary/*.sqlite 2>/dev/null | head -1)"
sqlite3 "$BKLIBRARY_DB" \
"SELECT ZTITLE, ZAUTHOR, ZGENRE, ZREADINGPROGRESS, ZASSETID
FROM ZBKLIBRARYASSET
WHERE ZTITLE IS NOT NULL AND (ZTITLE LIKE '%SEARCH_TERM%' OR ZAUTHOR LIKE '%SEARCH_TERM%')
ORDER BY ZLASTOPENDATE DESC;"
Replace SEARCH_TERM with the user's query.
BKLIBRARY_DB="$(ls ~/Library/Containers/com.apple.iBooksX/Data/Documents/BKLibrary/*.sqlite 2>/dev/null | head -1)"
sqlite3 "$BKLIBRARY_DB" \
"SELECT ZTITLE, ZAUTHOR, ZGENRE,
printf('%.0f%%', ZREADINGPROGRESS * 100) AS progress,
datetime(ZLASTOPENDATE + 978307200, 'unixepoch', 'localtime') AS last_opened
FROM ZBKLIBRARYASSET
WHERE ZTITLE IS NOT NULL
AND ZREADINGPROGRESS > 0.0
AND (ZISFINISHED IS NULL OR ZISFINISHED = 0)
ORDER BY ZLASTOPENDATE DESC;"
BKLIBRARY_DB="$(ls ~/Library/Containers/com.apple.iBooksX/Data/Documents/BKLibrary/*.sqlite 2>/dev/null | head -1)"
sqlite3 "$BKLIBRARY_DB" \
"SELECT ZTITLE, ZAUTHOR, ZGENRE,
datetime(ZDATEFINISHED + 978307200, 'unixepoch', 'localtime') AS finished_date
FROM ZBKLIBRARYASSET
WHERE ZISFINISHED = 1
ORDER BY ZDATEFINISHED DESC;"
AEANNOTATION_DB="$(ls ~/Library/Containers/com.apple.iBooksX/Data/Documents/AEAnnotation/*.sqlite 2>/dev/null | head -1)"
sqlite3 "$AEANNOTATION_DB" \
"SELECT ZANNOTATIONSELECTEDTEXT, ZANNOTATIONNOTE, ZANNOTATIONSTYLE,
datetime(ZANNOTATIONCREATIONDATE + 978307200, 'unixepoch', 'localtime') AS created
FROM ZAEANNOTATION
WHERE ZANNOTATIONDELETED = 0
AND ZANNOTATIONASSETID = 'ASSET_ID'
AND length(ZANNOTATIONSELECTEDTEXT) > 0
ORDER BY ZPLLOCATIONRANGESTART ASC;"
Replace ASSET_ID with the book's ZASSETID from the library query.
BKLIBRARY_DB="$(ls ~/Library/Containers/com.apple.iBooksX/Data/Documents/BKLibrary/*.sqlite 2>/dev/null | head -1)"
AEANNOTATION_DB="$(ls ~/Library/Containers/com.apple.iBooksX/Data/Documents/AEAnnotation/*.sqlite 2>/dev/null | head -1)"
sqlite3 "$AEANNOTATION_DB" \
"ATTACH DATABASE '$BKLIBRARY_DB' AS lib;
SELECT lib.ZBKLIBRARYASSET.ZTITLE, lib.ZBKLIBRARYASSET.ZAUTHOR,
ZAEANNOTATION.ZANNOTATIONSELECTEDTEXT, ZAEANNOTATION.ZANNOTATIONNOTE,
datetime(ZAEANNOTATION.ZANNOTATIONCREATIONDATE + 978307200, 'unixepoch', 'localtime') AS created
FROM ZAEANNOTATION
JOIN lib.ZBKLIBRARYASSET ON ZAEANNOTATION.ZANNOTATIONASSETID = lib.ZBKLIBRARYASSET.ZASSETID
WHERE ZAEANNOTATION.ZANNOTATIONDELETED = 0
AND length(ZAEANNOTATION.ZANNOTATIONSELECTEDTEXT) > 0
ORDER BY ZAEANNOTATION.ZANNOTATIONCREATIONDATE DESC
LIMIT 50;"
BKLIBRARY_DB="$(ls ~/Library/Containers/com.apple.iBooksX/Data/Documents/BKLibrary/*.sqlite 2>/dev/null | head -1)"
sqlite3 "$BKLIBRARY_DB" \
"SELECT c.ZTITLE, c.ZCOLLECTIONID, COUNT(m.Z_PK) AS book_count
FROM ZBKCOLLECTION c
LEFT JOIN ZBKCOLLECTIONMEMBER m ON m.Z_PK IN (
SELECT Z_PK FROM ZBKCOLLECTIONMEMBER
)
WHERE c.ZDELETEDFLAG = 0 AND c.ZTITLE IS NOT NULL
GROUP BY c.ZCOLLECTIONID
ORDER BY c.ZTITLE;"
BKLIBRARY_DB="$(ls ~/Library/Containers/com.apple.iBooksX/Data/Documents/BKLibrary/*.sqlite 2>/dev/null | head -1)"
sqlite3 "$BKLIBRARY_DB" \
"SELECT
COUNT(*) AS total_books,
SUM(CASE WHEN ZISFINISHED = 1 THEN 1 ELSE 0 END) AS finished,
SUM(CASE WHEN ZREADINGPROGRESS > 0 AND (ZISFINISHED IS NULL OR ZISFINISHED = 0) THEN 1 ELSE 0 END) AS in_progress,
SUM(CASE WHEN ZREADINGPROGRESS = 0 OR ZREADINGPROGRESS IS NULL THEN 1 ELSE 0 END) AS not_started,
printf('%.0f%%', AVG(ZREADINGPROGRESS) * 100) AS avg_progress
FROM ZBKLIBRARYASSET
WHERE ZTITLE IS NOT NULL;"
| Style value | Color |
|---|---|
| 1 | Green |
| 2 | Blue |
| 3 | Yellow |
| 4 | Pink |
| 5 | Purple |
| Type value | Meaning |
|---|---|
| 2 | Highlight |
| 3 | Bookmark |
Apple Books uses Core Data timestamps (seconds since 2001-01-01). To convert to human-readable:
datetime(TIMESTAMP_COLUMN + 978307200, 'unixepoch', 'localtime')
ZASSETID is the key that links books to their annotationsZREADINGPROGRESS is a float from 0.0 to 1.0ZISFINISHED is 1 when marked as finished, NULL or 0 otherwiseZLASTOPENDATE field tracks when the book was last openedZISSTOREAUDIOBOOK = 1)