A Calibre plugin for bidirectional sync with Hardcover.app.
- Sync reading status between Calibre and Hardcover (Want to Read, Currently Reading, Read, Paused, Did Not Finish)
- Sync metadata including ratings, reading progress, dates started/finished, and reviews
- Bidirectional sync - Push changes to Hardcover or pull from Hardcover to Calibre
- Calibre 6.0 or later (Qt6)
- A Hardcover.app account with an API token
- Download the latest release
.zipfile - In Calibre, go to Preferences → Plugins → Load plugin from file
- Select the downloaded
.zipfile - Restart Calibre
Warning
Your API token is stored in cleartext in Calibre's plugin configuration. Keep your Calibre configuration directory secure and do not share your plugins/Hardcover Sync.json file.
- Create a personal access token with the permissions Hardcover Sync needs. The link preselects
read:catalog,read:library,read:lists,read:me:content,write:library,write:reviews, andwrite:lists; review them before creating the token. - In Calibre, go to Preferences → Plugins → Hardcover Sync → Customize plugin
- Enter the new
hc_pat_…token and click Validate. Validation checks that the token can access the operations used by the plugin. - Configure column mappings for the data you want to sync
If you replace a legacy JWT, revoke it from your Hardcover API settings after the new personal access token is working.
Create these custom columns in Calibre (Preferences → Add your own columns):
| Column | Type | Suggested Name |
|---|---|---|
| Status | Text | #hc_status |
| Rating | Rating | #hc_rating |
| Progress (pages) | Integer | #hc_progress |
| Progress (%) | Float or integer | #hc_progress_pct |
| Date Started | Date | #hc_date_started |
| Date Finished | Date | #hc_date_read |
| Review | Long text | #hc_review |
You can use pre-defined Calibre columns as well (e.g. rating).
You can use either or both progress columns:
- Progress (pages) - Integer column for page number (e.g., 150)
- Progress (%) - Float or integer column for percentage (e.g., 50.0 or 50)
Hardcover stores page progress and calculates the percentage from the book's page count. When syncing to Hardcover with both columns populated, Progress (pages) takes precedence. Percentage progress is skipped with a warning when the linked Hardcover edition has no page count.
For a read-only column showing whether each book has a Hardcover identifier, see Tips & tricks.
I use yyyy-MM-dd. Check the docs for the format string.
Hardcover (toolbar button)
├── Set Status →
│ ├── Want to Read
│ ├── Currently Reading
│ ├── Read
│ ├── Paused
│ ├── Did Not Finish
│ └── Remove from Hardcover
├── Sync from Hardcover...
├── Sync to Hardcover...
├── Link to Hardcover...
├── View on Hardcover
├── Remove Hardcover Link
├── Customize plugin...
└── Help
Before syncing, books must be linked between Calibre and Hardcover:
- Select one or more books in Calibre
- Click Hardcover → Link to Hardcover...
- Search by ISBN (automatic) or title
- Select the matching Hardcover book
Sync from Hardcover: Pull your reading data from Hardcover into Calibre
- Fetches your Hardcover library
- Shows a preview of changes with checkboxes
- Only updates books that are already linked
Sync to Hardcover: Push your Calibre data to Hardcover
- Select books to sync
- Preview changes before applying
- Can add books to your Hardcover library or update existing books
Sync is intentionally non-destructive: an empty mapped value is treated as “no update” rather than a request to clear the value on the other service. Clear values manually on both sides when needed.
You can add a read-only custom column that shows a green check when a book has a hardcover identifier and a red X when it does not:
- In Calibre, go to Preferences → Add your own columns and add a custom column.
- Configure these fields:
- Lookup name:
hardcover(Calibre will refer to the created column as#hardcover) - Column heading:
Linked to Hardcover? - Column type:
Column built from other columns - Show checkmarks: enabled
- Template:
{identifiers:identifier_in_list(hardcover,yes,no)} - Sort/search column by:
Yes/No
- Lookup name:
- Click OK. Calibre will prompt you to restart.
This column is computed from the identifier stored in Calibre; it does not verify that the identifier still resolves on Hardcover.
Some books that you might have (omnibus volumes, educational papers, etc.) may not be on Hardcover, and if you're attempting to keep your collection synced as much as possible, having to weed through all of the "not synced" items can be frustrating if there are certain books you want to disregard.
Note
This is not a functional "ignore"; you can still select this book and attempt to link it. This is only a visual flag that you have chosen to ignore this, & to break it out of the default "yes/no" sorting.
If you want to mark a book as "Ignored" from syncing:
- Follow the steps above to add the custom column, or return to the Preferences → Add your own columns view & edit your existing column
- When you go to set or update the Template, click the Edit button next to the field
- In the Template: box, paste the following, and press "OK:
program:
if identifier_in_list(field('identifiers'), 'hardcover', '1', '') then
'yes'
elif str_in_list(field('tags'), ',', 'hardcover-sync-ignored', '1', '') then
'ignored'
else
'no'
fi
- Find any book that you'd like to flag as "ignored", edit its metadata, and add a tag of
hardcover-sync-ignored
- mise for tool management
# Install tools
mise install
# Install dependencies
uv sync
# Download Calibre source (for type hints)
just -f .calibre/justfile setupjust test # Run tests
just build # Build plugin zip
just install-plugin # Install to test Calibre instance
just calibre # Launch isolated test Calibre
just lint # Run linter and formatter
just typecheck # Run Pyright on production codeThis project uses prek for pre-commit hooks:
# Install hooks (run once after cloning)
uv run prek install
uv run prek install --hook-type commit-msg
# Run hooks manually
uv run prek run --all-files# Unit tests (mocked, no API token needed)
just test
# General integration tests (legacy JWT or PAT)
export HARDCOVER_API_TOKEN="your-token"
# PAT permission tests (optional)
export HARDCOVER_PAT_TOKEN="your-full-scope-hc_pat-token"
export HARDCOVER_RESTRICTED_PAT_TOKEN="your-PAT-without-write-reviews"
uv run pytest test/test_integration.py -vIntegration tests use read-only operations, skipped GraphQL mutations, and dry-run mode for mutations, so they won't modify your Hardcover library. The restricted PAT must include every Hardcover Sync scope except write:reviews.
The development environment and build tooling for this plugin is based on RobBrazier/calibre-plugins. Thanks to Rob Brazier for the excellent foundation including:
- Calibre source download scripts for IDE support
- Plugin bundling and packaging scripts
- Test infrastructure with Calibre/Qt mocking
- Isolated Calibre instance for development
He wrote the Hardcover metadata source plugin linked above. It's excellent.
The plugin icon uses icons from Material Design Icons (Apache 2.0 license).
GPL-3.0-or-later (same as Calibre)
