> ## Documentation Index
> Fetch the complete documentation index at: https://komga-sdk.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Book Content

> Access book pages, images, and reading positions

The generated Komga API 1.25 operations return non-throwing results. Pass path values as flat parameters and provide `{ client }` in the second argument.

## Get book pages

Retrieve metadata about every page in a book:

```typescript theme={null}
import { getBookPages } from 'komga-sdk';

const result = await getBookPages({ bookId: 'book-123' }, { client });

if (result.error) {
  console.error('Could not load page metadata', result.error);
} else {
  for (const page of result.data ?? []) {
    console.log(`${page.number}: ${page.width ?? 0}x${page.height ?? 0}`);
  }
}
```

## Get a specific page

### Get page image

`getBookPageByNumber` returns the page image as a `Blob | File`. Its optional query parameters (`convert`, `zero_based`, and `contentNegotiation`) are also flat:

```typescript theme={null}
import { getBookPageByNumber } from 'komga-sdk';

const result = await getBookPageByNumber(
  {
    bookId: 'book-123',
    pageNumber: 5,
    convert: 'jpeg',
  },
  { client },
);

if (result.error) {
  console.error('Page request failed', result.error);
} else if (result.data) {
  const imageUrl = URL.createObjectURL(result.data);
  // Display imageUrl, then revoke it when the image is no longer needed.
}
```

### Get raw page

`getBookPageRawByNumber` has a different generated response shape from the rendered page endpoint: its `result.data` is a `string`, not a `Blob | File`. Use it when your client needs the server's raw representation; do not pass it to `URL.createObjectURL`.

```typescript theme={null}
import { getBookPageRawByNumber } from 'komga-sdk';

const result = await getBookPageRawByNumber(
  { bookId: 'book-123', pageNumber: 5 },
  { client },
);

if (result.error) {
  console.error('Raw page request failed', result.error);
} else if (result.data) {
  console.log('Raw page payload:', result.data);
}
```

### Get page thumbnail

```typescript theme={null}
import { getBookPageThumbnailByNumber } from 'komga-sdk';

const result = await getBookPageThumbnailByNumber(
  { bookId: 'book-123', pageNumber: 5 },
  { client },
);

if (result.error) {
  console.error('Page thumbnail request failed', result.error);
} else if (result.data) {
  const thumbnailUrl = URL.createObjectURL(result.data);
}
```

## Reading progress and EPUB positions

`getBookPositions` returns an `R2Positions` object (`positions` plus `total`) for EPUB locations. This is separate from the page-based `ReadProgressDto` used by comic pages.

```typescript theme={null}
import {
  getBookPositions,
  getBookProgression,
  updateBookProgression,
} from 'komga-sdk';

const positions = await getBookPositions({ bookId: 'book-123' }, { client });
if (positions.error) throw positions.error;
console.log(`${positions.data?.total ?? 0} EPUB positions`);

const progression = await getBookProgression({ bookId: 'book-123' }, { client });
if (progression.error) throw progression.error;
if (progression.data) {
  console.log(progression.data.locator.href);
}
```

`updateBookProgression` accepts the complete `r2Progression` document (device, locator, and modified timestamp) and returns `204 No Content`. It is intended for the EPUB reader's Readium/OPDS progression, not for a page number:

```typescript theme={null}
const saved = await updateBookProgression(
  {
    bookId: 'book-123',
    r2Progression: {
      device: { id: 'web-reader', name: 'Web reader' },
      locator: {
        href: 'OEBPS/chapter-2.xhtml',
        type: 'application/xhtml+xml',
        locations: { progression: 0.42 },
      },
      modified: new Date().toISOString(),
    },
  },
  { client },
);
if (saved.error) console.error('Could not save EPUB progression', saved.error);
```

The page-oriented `markBookReadProgress` operation accepts `{ page }` or `{ completed: true }`; `deleteBookReadProgress` resets the book to unread. Both are accepted/204 operations and return no updated book.

## Book thumbnails

### Get book thumbnail

```typescript theme={null}
import { getBookThumbnail } from 'komga-sdk';

const result = await getBookThumbnail({ bookId: 'book-123' }, { client });

if (result.error) {
  console.error('Book thumbnail request failed', result.error);
} else if (result.data) {
  const thumbnailUrl = URL.createObjectURL(result.data);
}
```

### List available thumbnails

```typescript theme={null}
import { getBookThumbnails } from 'komga-sdk';

const result = await getBookThumbnails({ bookId: 'book-123' }, { client });

if (result.error) {
  console.error('Could not list thumbnails', result.error);
} else {
  for (const thumbnail of result.data ?? []) {
    console.log(`${thumbnail.id}: ${thumbnail.selected ? 'selected' : 'available'}`);
  }
}
```

### Get a specific thumbnail

```typescript theme={null}
import { getBookThumbnailById } from 'komga-sdk';

const result = await getBookThumbnailById(
  { bookId: 'book-123', thumbnailId: 'thumb-456' },
  { client },
);

if (result.error) {
  console.error('Thumbnail request failed', result.error);
} else if (result.data) {
  const thumbnailUrl = URL.createObjectURL(result.data);
}
```

### Upload a custom thumbnail

`addUserUploadedBookThumbnail` takes the file as the flat `file` parameter and accepts `selected` as a query parameter:

```typescript theme={null}
import { addUserUploadedBookThumbnail } from 'komga-sdk';

const file = new File([imageBlob], 'custom-thumb.jpg', { type: 'image/jpeg' });
const result = await addUserUploadedBookThumbnail(
  { bookId: 'book-123', file, selected: true },
  { client },
);

if (result.error) {
  console.error('Thumbnail upload failed', result.error);
} else if (result.data) {
  console.log(`Uploaded thumbnail: ${result.data.id}`);
}
```

### Delete a custom thumbnail

The delete operation returns an accepted response without a data payload:

```typescript theme={null}
import { deleteUserUploadedBookThumbnail } from 'komga-sdk';

const result = await deleteUserUploadedBookThumbnail(
  { bookId: 'book-123', thumbnailId: 'thumb-456' },
  { client },
);

if (result.error) {
  console.error('Thumbnail deletion failed', result.error);
} else {
  console.log('Thumbnail deleted');
}
```

### Mark a thumbnail as selected

```typescript theme={null}
import { markBookThumbnailSelected } from 'komga-sdk';

const result = await markBookThumbnailSelected(
  { bookId: 'book-123', thumbnailId: 'thumb-456' },
  { client },
);

if (result.error) {
  console.error('Could not select thumbnail', result.error);
} else {
  console.log('Thumbnail selected');
}
```

## WebPub manifests

The manifest operations return WebPub publication data rather than binary image data:

```typescript theme={null}
import {
  getBookWebPubManifest,
  getBookWebPubManifestDivina,
  getBookWebPubManifestEpub,
  getBookWebPubManifestPdf,
} from 'komga-sdk';

const generic = await getBookWebPubManifest({ bookId: 'book-123' }, { client });
const divina = await getBookWebPubManifestDivina({ bookId: 'book-123' }, { client });
const epub = await getBookWebPubManifestEpub({ bookId: 'book-123' }, { client });
const pdf = await getBookWebPubManifestPdf({ bookId: 'book-123' }, { client });

for (const [name, result] of Object.entries({ generic, divina, epub, pdf })) {
  if (result.error) {
    console.error(`${name} manifest failed`, result.error);
  } else if (result.data) {
    console.log(`${name} manifest loaded`, result.data);
  }
}
```

## EPUB resources

`getBookEpubResource` returns the resource body as a string:

```typescript theme={null}
import { getBookEpubResource } from 'komga-sdk';

const result = await getBookEpubResource(
  { bookId: 'book-123', resource: 'OEBPS/images/cover.jpg' },
  { client },
);

if (result.error) {
  console.error('EPUB resource request failed', result.error);
} else if (result.data) {
  console.log(result.data);
}
```

## Response and cleanup semantics

Rendered pages, page thumbnails, book thumbnails, and thumbnail-by-ID responses are `Blob | File`. Create an object URL only for those binary responses and revoke it when the image is removed from the UI. WebPub manifest operations return a `WpPublicationDto`, while `getBookEpubResource` and raw-page operations are generated as `string` responses. A `404` commonly means the requested page/resource does not exist; a `400` can indicate an invalid page number, conversion, or resource path.

## Common workflows

### Build a simple page viewer

```typescript theme={null}
import { getBookPages, getBookPageByNumber } from 'komga-sdk';

async function createPageViewer(bookId: string) {
  const pagesResult = await getBookPages({ bookId }, { client });
  if (pagesResult.error) throw pagesResult.error;

  const pages = pagesResult.data ?? [];
  let currentPage = 1;

  async function showPage(pageNum: number) {
    if (pageNum < 1 || pageNum > pages.length) return;

    const result = await getBookPageByNumber(
      { bookId, pageNumber: pageNum },
      { client },
    );
    if (result.error) throw result.error;
    if (!result.data) return;

    const imageUrl = URL.createObjectURL(result.data);
    // Display imageUrl in your viewer.
    currentPage = pageNum;
  }

  return {
    showPage,
    nextPage: () => showPage(currentPage + 1),
    prevPage: () => showPage(currentPage - 1),
    totalPages: pages.length,
  };
}
```

### Create a thumbnail gallery

```typescript theme={null}
import { getBookThumbnails, getBookThumbnailById } from 'komga-sdk';

async function createThumbnailGallery(bookId: string) {
  const result = await getBookThumbnails({ bookId }, { client });
  if (result.error) throw result.error;

  const thumbnails = [];
  for (const thumbnail of result.data ?? []) {
    const thumbnailResult = await getBookThumbnailById(
      { bookId, thumbnailId: thumbnail.id },
      { client },
    );
    if (thumbnailResult.error) throw thumbnailResult.error;
    if (thumbnailResult.data) {
      thumbnails.push({
        id: thumbnail.id,
        url: URL.createObjectURL(thumbnailResult.data),
        selected: thumbnail.selected,
      });
    }
  }

  return thumbnails;
}
```

### Display a WebPub reader

```typescript theme={null}
import { getBookWebPubManifest } from 'komga-sdk';

async function loadWebPubReader(bookId: string) {
  const result = await getBookWebPubManifest({ bookId }, { client });
  if (result.error) throw result.error;
  if (!result.data) return;

  // Pass the publication data to a WebPub-compatible reader.
  return result.data;
}
```

## Next steps

> **[Books](/guides/books)**
>
> Learn about book management operations.

> **[Thumbnails](/guides/thumbnails)**
>
> Deep dive into thumbnail management across all content types.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.