M A N D A L I V I A
Obsidian Lab All Notes

Add Books to Your Vault with Google Books and Claude

The Problem

Adding a book to my Obsidian vault meant opening Goodreads or Google Books in a browser, copying the title, author, ISBN, and cover URL, then pasting them into a template and fixing the formatting. I used the Obsidian Web Clipper for a while, which helped, but it still required switching contexts — open browser, find the right page, clip it, go back to Obsidian, clean up the result.

For something I do a few times a week, the friction was enough that I’d skip it. Books I was reading wouldn’t get notes until weeks later, if at all.

The Solution

A Claude Code skill that does the lookup and note creation in one step. Say /add-book Talking to Strangers by Malcolm Gladwell, and it searches Google Books, shows you the matches, asks a couple of questions (reading status, any context), and writes the note.

The architecture splits the work: a dumb script handles the API call, Claude handles the reasoning.

  • google-books-search.sh — a bash script that calls the Google Books API and pipes the response through jq to extract only the fields that matter: title, authors, year, ISBN, cover URL, publisher, page count
  • Claude skill — parses the user’s natural language input, calls the script, presents results, and creates a properly formatted book note with wikilinked authors and frontmatter

How To

The search script

The script lives alongside the skill definition. It takes a query string, hits the Google Books API (no API key needed for basic searches), and returns clean JSON:

#!/usr/bin/env bash
set -euo pipefail
 
QUERY="${1:?Usage: google-books-search.sh \"query string\"}"
API_URL="https://www.googleapis.com/books/v1/volumes"
 
curl -s "${API_URL}?q=$(printf '%s' "$QUERY" | jq -sRr @uri)&maxResults=5" | jq '[
  .items[]? | .volumeInfo | {
    title: (.title + if .subtitle then ": " + .subtitle else "" end),
    authors: (.authors // []),
    year: (.publishedDate // "" | split("-")[0]),
    isbn13: ([(.industryIdentifiers // [])[] | select(.type == "ISBN_13") | .identifier][0] // ""),
    isbn10: ([(.industryIdentifiers // [])[] | select(.type == "ISBN_10") | .identifier][0] // ""),
    cover: ((.imageLinks.thumbnail // "") | gsub("http:"; "https:") | gsub("zoom=1"; "zoom=3")),
    publisher: (.publisher // ""),
    pageCount: (.pageCount // 0)
  }
]'

The raw Google Books response is massive — descriptions, access info, sale info, preview links, maturity ratings. The jq filter reduces each result to about 8 fields. That’s the difference between sending thousands of tokens through Claude for parsing versus a few dozen.

The skill prompt

The skill prompt tells Claude how to handle the interaction:

  1. Parse the user’s input — split “by” to separate title from author, detect ISBNs
  2. Build the Google Books query (intitle:, inauthor:, isbn:)
  3. Run the script and show a numbered list of results
  4. Ask for reading status (To Read / Reading / Read) and optional context
  5. Create the note using the vault’s book template

The book template

The note frontmatter captures the metadata that matters for filtering and display:

---
Title: "Full Title Including Subtitle"
Author: "[Author Name](/obsidian/author-name/)"
year: 2025
Cover: "https://..."
isbn: "9781234567890"
date-created: 2026-03-12
Status: To Read
Rating:
tags:
  - Type/book
read:
---

Authors get wikilinked so they accumulate backlinks as you add more books. Rating is a bare number (not a string) so Obsidian’s Dataview can sort by it. The cover URL points to Google Books’ higher-resolution thumbnail.

What I Learned

jq as a token-saving layer. The Google Books API returns 15-20KB of JSON per query. After jq, it’s under 1KB. That’s not just faster — it means Claude doesn’t waste reasoning tokens parsing fields it’ll never use. Anytime a skill calls an external API, piping through jq first is worth considering.

Google Books API doesn’t require a key for reads. Basic volume searches work unauthenticated. You’ll hit rate limits eventually, but for adding a few books a day it’s a non-issue. The QuickAdd script I originally looked at required an API key in the plugin settings — unnecessary complexity for this use case.

Keep the script with the skill. I considered putting the script in my global ~/bin directory, but it’s single-purpose. Bundling it in the skill directory (.claude/skills/add-book/) keeps the skill self-contained and portable.

Caveats

  • Google Books metadata isn’t always complete — some editions lack ISBNs or cover images. The skill falls back to OpenLibrary’s default cover when there’s no thumbnail.
  • Multiple editions of the same book show up as separate results. You pick the one you want.
  • No API key means you’re subject to anonymous rate limits — fine for personal use, not for batch-importing a library.

Voice-Driven Time Tracking with Toggl and Claude

Keep Exploring