bookstack-client
Python 3.14 CLI and library for the BookStack REST API
Overview
bookstack-client is a Python package for working with the BookStack REST API from scripts or the command line. It provides a small typed client, environment-based configuration, JSON output, and parallel batch reads.
BookStack authentication uses:
Authorization: Token <token_id>:<token_secret>
Key Features
| Feature | Description |
|---|---|
| CLI + Library | Use as a command-line tool or Python package |
| Python 3.14 | Built and tested for Python 3.14+ |
| Environment Config | Reads BOOKSTACK_URL, BOOKSTACK_TOKEN_ID, and BOOKSTACK_TOKEN_SECRET |
| .env Support | Loads credentials from a local .env file when environment variables are not set |
| CRUD Helpers | Convenience methods for list, get, create, update, and delete |
| Raw Requests | Access any BookStack API endpoint with request() |
| Parallel Reads | Fetch multiple resources concurrently with get_many() or batch-get |
Supported Operations
List resources books, shelves, pages, chapters, users, roles, attachments, images
List all pages Auto-paginate any resource with list --all
Read item GET /api/<resource>/<id>
Create item POST /api/<resource>
Update item PUT /api/<resource>/<id>
Delete item DELETE /api/<resource>/<id>
Export item GET /api/<resource>/<id>/export/<format>
Search GET /api/search
Tags GET /api/tags/names, /api/tags/values-for-name
Recycle bin List, restore, or permanently delete deleted content
Permissions Read or set content-level permissions
Raw request Any BookStack API path
Batch reads Parallel get by IDs
Installation
From PyPI
pip install bookstack-client
From Source
git clone https://github.com/seifreed/bookstack-cli.git
cd bookstack-cli
python3 -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -e .
Configuration
Environment Variables
export BOOKSTACK_URL="https://bookstack.example.com"
export BOOKSTACK_TOKEN_ID="token-id"
export BOOKSTACK_TOKEN_SECRET="token-secret"
.env File
BOOKSTACK_URL=https://bookstack.example.com
BOOKSTACK_TOKEN_ID=token-id
BOOKSTACK_TOKEN_SECRET=token-secret
Environment variables override values loaded from .env.
Quick Start
# List books
bookstack list books --count 20 --sort +name
# Read a page
bookstack get pages 1
# Fetch pages in parallel
bookstack batch-get pages 1 2 3 --workers 3
Local BookStack Testing
This repository includes a Docker Compose setup for a disposable local BookStack instance:
scripts/bookstack-local.sh up
The script starts BookStack and MariaDB, creates an admin user, creates an API token, and writes a local .env file for the CLI.
Run the live CLI regression test with:
BOOKSTACK_LIVE_TESTS=1 uv run pytest -q tests/integration
Stop the stack with:
scripts/bookstack-local.sh down
Usage
Command Line Interface
# Create a page
bookstack create pages --data '{"book_id":1,"name":"API page","markdown":"Hello"}'
# Update a page
bookstack update pages 1 --data '{"name":"Renamed"}'
# Delete a page
bookstack delete pages 1
# Call a raw endpoint
bookstack request GET /api/system
# Multipart upload
bookstack request POST /api/image-gallery \
--field type=gallery \
--field uploaded_to=1 \
--file image=photo.png
# Search content
bookstack search "deprecated runbook"
# List every tag name, or values for one tag
bookstack tags
bookstack tags --name status
# Manage the recycle bin
bookstack recycle-bin list --count 20 --offset 0
bookstack recycle-bin restore 4
bookstack recycle-bin destroy 4
# Read or set content-level permissions
bookstack permissions get bookshelf 1
bookstack permissions set book 1 --data '{"fallback_permissions":{"inheriting":false}}'
# Download an export directly
bookstack export pages 1 pdf -o page.pdf
# Fetch every page of a resource automatically
bookstack list books --all
Global Options
These apply to every command and must come before the subcommand name:
| Option | Description |
|---|---|
--env-file <path> |
dotenv file to read; environment variables still override it (default .env) |
--timeout <seconds> |
HTTP timeout in seconds (default 30) |
--json |
Print compact JSON instead of indented |
--version |
Print the installed version and exit |
bookstack --env-file prod.env --timeout 60 --json list books
Available Commands
| Command | Description |
|---|---|
bookstack list <resource> |
List BookStack resources |
bookstack get <resource> <id> |
Fetch one resource by ID |
bookstack batch-get <resource> <ids...> |
Fetch several resources concurrently |
bookstack create <resource> --data <json> |
Create a resource |
bookstack update <resource> <id> --data <json> |
Update a resource |
bookstack delete <resource> <id> |
Delete a resource |
bookstack request <method> <path> |
Send a raw API request |
bookstack search <query> |
Search content |
bookstack tags |
List tag names, or values for --name |
bookstack recycle-bin list|restore|destroy |
Manage deleted content |
bookstack permissions get|set <type> <id> |
Read or set content-level permissions |
bookstack export <resource> <id> <format> -o <path> |
Download a resource export |
Listing Options
| Option | Description |
|---|---|
--count <n> |
Limit result count, or page size when --all is used |
--offset <n> |
Offset list results (not usable with --all) |
--sort <field> |
Sort with BookStack syntax, such as +name |
--filter FIELD=VALUE |
Add BookStack filter[...] query parameters |
--all |
Fetch every page automatically |
Raw Request Options
| Option | Description |
|---|---|
--data <json> |
Send a JSON request body |
--field NAME=VALUE |
Add a multipart form field |
--file NAME=PATH |
Add a multipart file field |
-o, --output <path> |
Write raw response bytes to a file |
Task Recipes
These recipes use the current CLI commands and work against any configured BookStack instance.
Export All Books
Export every visible book as a ZIP archive:
mkdir -p exports/books
bookstack --json list books --all \
| python3 -c 'import json,sys; [print(book["id"]) for book in json.load(sys.stdin)]' \
| while read -r id; do
bookstack export books "$id" zip -o "exports/books/${id}.zip"
done
Use another export format by changing the last argument:
bookstack export books 1 pdf -o book-1.pdf
bookstack export books 1 markdown -o book-1.md
bookstack export books 1 html -o book-1.html
bookstack export books 1 plaintext -o book-1.txt
Create Pages From Markdown
Create a page from a local Markdown file:
python3 - <<'PY' > /tmp/page.json
import json
from pathlib import Path
print(json.dumps({
"book_id": 1,
"name": "Runbook",
"markdown": Path("runbook.md").read_text(encoding="utf-8"),
}))
PY
bookstack create pages --data "$(cat /tmp/page.json)"
Create the page inside a chapter instead:
python3 - <<'PY' > /tmp/page.json
import json
from pathlib import Path
print(json.dumps({
"chapter_id": 10,
"name": "Incident Response",
"markdown": Path("incident-response.md").read_text(encoding="utf-8"),
}))
PY
bookstack create pages --data "$(cat /tmp/page.json)"
Upload Images
Upload an image to a page and get reusable HTML/Markdown snippets in the response:
bookstack request POST /api/image-gallery \
--field type=gallery \
--field uploaded_to=1 \
--field name=diagram.png \
--file image=diagram.png
Download an uploaded image by ID:
bookstack request GET /api/image-gallery/42/data -o image.png
Migrate Content
Move content between BookStack instances using BookStack ZIP exports/imports.
On the source instance:
bookstack export books 1 zip -o book-1.zip
On the target instance, after switching .env or environment variables:
import_id="$(
bookstack --json request POST /api/imports --file file=book-1.zip \
| python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])'
)"
bookstack request POST "/api/imports/${import_id}"
For chapter or page imports, provide the target parent:
bookstack request POST "/api/imports/${import_id}" \
--data '{"parent_type":"book","parent_id":1}'
Partial Backup
Back up structure and selected binary exports without dumping the whole database:
mkdir -p backup/json backup/exports
for resource in books chapters pages shelves attachments users roles; do
bookstack --json list "$resource" > "backup/json/${resource}.json"
done
bookstack --json request GET /api/system > backup/json/system.json
bookstack --json tags > backup/json/tag-names.json
bookstack --json list books --all \
| python3 -c 'import json,sys; [print(book["id"]) for book in json.load(sys.stdin)]' \
| while read -r id; do
bookstack export books "$id" zip -o "backup/exports/book-${id}.zip"
done
Find Old Content
Find old or stale pages using BookStack list filters:
bookstack list pages \
--filter updated_at:lt=2025-01-01 \
--sort -updated_at \
--count 50
Search content by text:
bookstack search "deprecated" --count 20
Search by BookStack query syntax:
bookstack search "{updated_by:me} runbook" --page 1 --count 20
Automate Permissions
Read current content permissions for a book:
bookstack permissions get book 1
Set explicit fallback permissions while leaving role overrides unchanged:
bookstack permissions set book 1 --data '{
"fallback_permissions": {
"inheriting": false,
"view": true,
"create": false,
"update": false,
"delete": false
}
}'
Apply a role override:
bookstack permissions set book 1 --data '{
"role_permissions": [
{
"role_id": 2,
"view": true,
"create": true,
"update": true,
"delete": false
}
]
}'
Use page, book, chapter, or bookshelf as the content type — not shelf, which BookStack rejects.
Python Library
Basic Usage
from bookstack_cli import BookStackClient
client = BookStackClient.from_env()
books = client.list("books", count=20)
page = client.get("pages", 1)
pages = client.get_many("pages", [1, 2, 3], workers=3)
Create and Update
from bookstack_cli import BookStackClient
client = BookStackClient.from_env()
created = client.create("pages", {
"book_id": 1,
"name": "API page",
"markdown": "Hello from Python",
})
updated = client.update("pages", created["id"], {"name": "Renamed page"})
Raw API Requests
from bookstack_cli import BookStackClient
client = BookStackClient.from_env()
system = client.request("GET", "/api/system")
Search, Tags, Recycle Bin, Permissions & Export
from bookstack_cli import BookStackClient
client = BookStackClient.from_env()
all_books = client.list_all("books") # auto-paginates
results = client.search("runbook", count=20)
tag_names = client.tags()
tag_values = client.tags(name="status")
deleted = client.recycle_bin_list()
client.recycle_bin_restore(deleted["data"][0]["id"])
client.recycle_bin_destroy(4)
perms = client.get_content_permissions("bookshelf", 1)
client.set_content_permissions("book", 1, {"fallback_permissions": {"inheriting": False}})
pdf = client.export("pages", 1, "pdf")
open("page.pdf", "wb").write(pdf.body)
Architecture
Five small modules, each with one job and a strict, one-directional dependency chain:
cli.py argparse wiring + dispatch only. Talks to BookStack only
through BookStackClient's public methods.
|
v
client.py One method per BookStack resource operation. Owns BookStack's
URL and resource-shape conventions.
|
v
transport.py HTTP encoding (JSON/multipart), headers, the raw urllib call,
and mapping failures to BookStackAPIError.
|
v
config.py Reads BOOKSTACK_* settings from the environment/.env file.
errors.py The one error type API failures surface as.
config.py and errors.py have no internal dependencies. Nothing outside
transport.py imports urllib directly, and nothing outside cli.py builds
an argparse parser. cli.py never imports transport.py — it only ever
sees BookStackClient.
Requirements
- Python 3.14+
- No runtime dependencies
- See pyproject.toml for package metadata
Quality Gate
Run all local non-live checks with:
scripts/quality.sh
The gate runs black, ruff, mypy, bandit, pip-audit, and the non-integration pytest suite.
Contributing
Contributions are welcome.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Support the Project
If this project is useful in your workflows, you can support development:
License
This project is licensed under the MIT license. See pyproject.toml.
Attribution
- Author: Seif Reed | @seifreed
- Repository: github.com/seifreed/bookstack-cli
Built for practical BookStack automation
Metadata
Release files for bookstack-client 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| bookstack_client-0.1.0.tar.gz | 57.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| bookstack_client-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 73.0 kB
Release files / bookstack_client-0.1.0.tar.gz
| Download URL | bookstack_client-0.1.0.tar.gz |
|---|---|
| Size | 57.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3c82f4ebcdcea480bf2b2c19c551b52b3ce7b212c994a04b80ef0001e10d6325
|
|
BLAKE2b-256 checksum How to use checksums |
7db74d8c9e104e486d97bea12b3178cccc93bfcbb6b7a12fba857c2b321f0785
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 10, 2026.
Transparency logRelease files / bookstack_client-0.1.0-py3-none-any.whl
| Download URL | bookstack_client-0.1.0-py3-none-any.whl |
|---|---|
| Size | 15.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3f490b03dce90780021b5e366c734926ace17dec178e523c43a5841b38f9a133
|
|
BLAKE2b-256 checksum How to use checksums |
c80e0f23311f8542fe9560c0ebc0bcd84c7dcca72c2b3143cfdad7a79428b12f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 10, 2026.
Transparency log