> ## 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.

# Library Maintenance

> Scan, analyze, and maintain your Komga libraries

These examples use generated Komga API 1.25 operations. Values such as `libraryId` and `deep` are flat parameters, followed by `{ client }`. Generated results are non-throwing by default.

> [!WARNING]
>
> Most operations in this guide require **ADMIN** role.

## Library CRUD operations

### List all libraries

`getLibraries` accepts `{ client }` as its options argument because it has no parameter object:

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

const result = await getLibraries({ client });

if (result.error) {
  console.error('Could not list libraries', result.error);
} else {
  for (const library of result.data ?? []) {
    console.log(`${library.name}: ${library.root}`);
  }
}
```

### Get a library by ID

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

const result = await getLibraryById({ libraryId: 'library-123' }, { client });

if (result.error) {
  console.error('Library lookup failed', result.error);
} else if (result.data) {
  console.log(`Library: ${result.data.name}`);
  console.log(`Root path: ${result.data.root}`);
  console.log(`Scan interval: ${result.data.scanInterval}`);
}
```

### Update a library

`updateLibraryById` returns `void` data on success:

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

const result = await updateLibraryById(
  {
    libraryId: 'library-123',
    libraryUpdateDto: {
      name: 'Updated Library Name',
      importComicInfoBook: true,
      importComicInfoSeries: true,
      scanInterval: 'EVERY_6H',
    },
  },
  { client },
);

if (result.error) {
  console.error('Library update failed', result.error);
} else {
  console.log('Library updated');
}
```

### Delete a library

This removes the library configuration but does not delete underlying files. The successful response has no data:

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

const result = await deleteLibraryById({ libraryId: 'library-123' }, { client });

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

## Library maintenance operations

### Scan library

`deep` is an optional flat query parameter:

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

const result = await libraryScan(
  { libraryId: 'library-123', deep: true },
  { client },
);

if (result.error) {
  console.error('Library scan failed', result.error);
} else {
  console.log('Library scan queued');
}
```

> [!NOTE]
>
> Library scans run as background tasks. The accepted response has no useful data payload; monitor Komga tasks separately.

Do not assume that the scan has finished when the request resolves. If you need a strict sequence (scan → analyze → refresh), observe the server's task state between calls; otherwise, these operations may overlap.

### Analyze library

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

const result = await libraryAnalyze({ libraryId: 'library-123' }, { client });
if (result.error) {
  console.error('Library analysis failed', result.error);
}
```

### Refresh metadata

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

const result = await libraryRefreshMetadata({ libraryId: 'library-123' }, { client });
if (result.error) {
  console.error('Library metadata refresh failed', result.error);
}
```

### Empty trash

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

const result = await libraryEmptyTrash({ libraryId: 'library-123' }, { client });
if (result.error) {
  console.error('Empty trash failed', result.error);
}
```

## Book maintenance

### Analyze a book

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

const result = await bookAnalyze({ bookId: 'book-123' }, { client });
if (result.error) {
  console.error('Book analysis failed', result.error);
}
```

### Refresh book metadata

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

const result = await bookRefreshMetadata({ bookId: 'book-123' }, { client });
if (result.error) {
  console.error('Book metadata refresh failed', result.error);
}
```

### Delete a book file

> [!WARNING]
>
> This permanently deletes the book file from disk and cannot be undone.

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

const result = await deleteBookFile({ bookId: 'book-123' }, { client });
if (result.error) {
  console.error('Book file deletion failed', result.error);
}
```

### Delete series files

> [!WARNING]
>
> This permanently deletes all book files in the series from disk and cannot be undone.

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

const result = await deleteSeriesFile({ seriesId: 'series-123' }, { client });
if (result.error) {
  console.error('Series file deletion failed', result.error);
}
```

### Update book metadata

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

const result = await updateBookMetadata(
  {
    bookId: 'book-123',
    bookMetadataUpdateDto: {
      title: 'New Title',
      summary: 'Updated summary',
      authors: [{ name: 'Author Name', role: 'writer' }],
    },
  },
  { client },
);

if (result.error) {
  console.error('Book metadata update failed', result.error);
}
```

### Batch update book metadata

`updateBookMetadataByBatch` takes a flat `body` map keyed by book ID. It does not accept an `ids` plus `metadata` object:

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

const result = await updateBookMetadataByBatch(
  {
    body: {
      'book-1': { tags: ['batch-updated'] },
      'book-2': { tags: ['batch-updated'] },
    },
  },
  { client },
);

if (result.error) {
  console.error('Batch metadata update failed', result.error);
}
```

### Metadata reset and batch semantics

Book and series metadata updates are partial: omitted fields remain unchanged. The API update contract permits `null` to unset a scalar metadata field, while an empty array such as `tags: []` clears a collection. Lock flags are independent and must be updated explicitly. Both single-item metadata updates and `updateBookMetadataByBatch` return `204 No Content`; re-fetch items if the caller needs the resulting DTO.

The batch endpoint's flat `body` is a map from book ID to its own `BookMetadataUpdateDto`. It is not an `{ ids, metadata }` envelope; each map value may contain a different partial update.

## Common workflows

### Full library refresh

The maintenance operations return accepted responses, so check `error` after each request:

```typescript theme={null}
import {
  libraryScan,
  libraryAnalyze,
  libraryRefreshMetadata,
  booksRegenerateThumbnails,
} from 'komga-sdk';

async function fullLibraryMaintenance(libraryId: string) {
  const scan = await libraryScan({ libraryId }, { client });
  if (scan.error) throw scan.error;

  const analysis = await libraryAnalyze({ libraryId }, { client });
  if (analysis.error) throw analysis.error;

  const refresh = await libraryRefreshMetadata({ libraryId }, { client });
  if (refresh.error) throw refresh.error;

  const thumbnails = await booksRegenerateThumbnails(undefined, { client });
  if (thumbnails.error) throw thumbnails.error;

  console.log('Maintenance tasks queued');
}
```

`libraryAnalyze`, `libraryRefreshMetadata`, and thumbnail regeneration are also queued administrative tasks. A maintenance runner should record which requests were accepted and let Komga's task system report completion rather than treating a successful HTTP response as completed work.

### Batch operations on a library

Use the `SearchConditionLibraryId` condition inside `bookSearch` to list only books from one library:

```typescript theme={null}
import { getBooks, updateBookMetadataByBatch } from 'komga-sdk';

async function batchTagUntaggedBooks(libraryId: string) {
  const result = await getBooks(
    {
      unpaged: true,
      bookSearch: {
        condition: {
          libraryId: { operator: 'is', value: libraryId },
        },
      },
    },
    { client },
  );
  if (result.error) throw result.error;

  const untaggedBooks = (result.data?.content ?? []).filter(
    (book) => !book.metadata.tags || book.metadata.tags.length === 0,
  );
  if (untaggedBooks.length === 0) return;

  const update = await updateBookMetadataByBatch(
    {
      body: Object.fromEntries(
        untaggedBooks.map((book) => [book.id, { tags: ['needs-tagging'] }]),
      ),
    },
    { client },
  );
  if (update.error) throw update.error;

  console.log(`Tagged ${untaggedBooks.length} books`);
}
```

### Library statistics

```typescript theme={null}
import { getLibraries, getSeries, getBooks } from 'komga-sdk';

async function getLibraryStatistics() {
  const librariesResult = await getLibraries({ client });
  if (librariesResult.error) throw librariesResult.error;

  const stats = [];
  for (const library of librariesResult.data ?? []) {
    const [series, books] = await Promise.all([
      getSeries(
        {
          unpaged: true,
          seriesSearch: {
            condition: {
              libraryId: { operator: 'is', value: library.id },
            },
          },
        },
        { client },
      ),
      getBooks(
        {
          unpaged: true,
          bookSearch: {
            condition: {
              libraryId: { operator: 'is', value: library.id },
            },
          },
        },
        { client },
      ),
    ]);

    if (series.error) throw series.error;
    if (books.error) throw books.error;

    stats.push({
      name: library.name,
      seriesCount: series.data?.totalElements ?? 0,
      bookCount: books.data?.totalElements ?? 0,
    });
  }

  return stats;
}
```

### Maintenance scheduler

```typescript theme={null}
import {
  libraryScan,
  libraryAnalyze,
  libraryRefreshMetadata,
  booksRegenerateThumbnails,
} from 'komga-sdk';

class MaintenanceScheduler {
  constructor(private readonly libraryId: string) {}

  async runDailyMaintenance() {
    const result = await libraryScan({ libraryId: this.libraryId }, { client });
    if (result.error) throw result.error;
  }

  async runWeeklyMaintenance() {
    const result = await libraryAnalyze({ libraryId: this.libraryId }, { client });
    if (result.error) throw result.error;
  }

  async runMonthlyMaintenance() {
    const refresh = await libraryRefreshMetadata(
      { libraryId: this.libraryId },
      { client },
    );
    if (refresh.error) throw refresh.error;

    const thumbnails = await booksRegenerateThumbnails(undefined, { client });
    if (thumbnails.error) throw thumbnails.error;
  }
}
```

## Error handling

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

const result = await libraryScan({ libraryId: 'library-123' }, { client });

if (result.error) {
  switch (result.response?.status) {
    case 401:
      console.error('Not authenticated');
      break;
    case 403:
      console.error('Admin access required');
      break;
    case 404:
      console.error('Library not found');
      break;
    default:
      console.error('Scan failed', result.error);
  }
}
```

## Next steps

> **[Libraries](/guides/libraries)**
>
> Learn about library creation and configuration.

> **[Admin](/guides/admin)**
>
> Server administration and settings.

> **[Book Content](/guides/book-content)**
>
> Access book pages and thumbnails.

> **[Series Discovery](/guides/series-discovery)**
>
> Browse and discover series.


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