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

# Series Discovery

> Find new series, view latest additions, and explore your library

These examples use generated Komga API 1.25 operations. Query and path values are flat parameters; `{ client }` is the options argument. Results are non-throwing unless you explicitly add your own throwing boundary.

## Browse latest series

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

const result = await getSeriesLatest({ page: 0, size: 20 }, { client });

if (result.error) {
  console.error('Could not load latest series', result.error);
} else {
  for (const series of result.data?.content ?? []) {
    console.log(`${series.metadata.title} (added: ${series.created})`);
  }
}
```

## Get new series

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

const result = await getSeriesNew({ page: 0, size: 20 }, { client });

if (result.error) {
  console.error('Could not load new series', result.error);
} else {
  console.log(`Found ${result.data?.totalElements ?? 0} new series`);
}
```

## Get updated series

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

const result = await getSeriesUpdated({ page: 0, size: 20 }, { client });

if (result.error) {
  console.error('Could not load updated series', result.error);
} else {
  for (const series of result.data?.content ?? []) {
    console.log(`${series.metadata.title} (updated: ${series.lastModified})`);
  }
}
```

### Scope discovery results

The latest, new, and updated endpoints accept `library_id` (an array), `deleted`, `oneshot`, `unpaged`, `page`, and `size`. They are already sorted by the server's discovery rule and do not expose a `sort` parameter. `unpaged: true` is useful for a small export but can be expensive for a large library.

```typescript theme={null}
const newInLibrary = await getSeriesNew(
  {
    library_id: ['library-123'],
    deleted: false,
    oneshot: false,
    page: 0,
    size: 30,
  },
  { client },
);
if (newInLibrary.error) throw newInLibrary.error;
```

## Get series release dates

`getSeriesReleaseDates` returns an array of ISO date strings, not a paged response:

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

const result = await getSeriesReleaseDates(undefined, { client });

if (result.error) {
  console.error('Could not load release dates', result.error);
} else {
  for (const releaseDate of result.data ?? []) {
    console.log(releaseDate);
  }
}
```

Pass `library_id` or `collection_id` when release dates should be scoped. The response remains an array of ISO date strings, not a `PageSeriesDto`:

```typescript theme={null}
const dates = await getSeriesReleaseDates(
  { library_id: ['library-123'], collection_id: 'collection-123' },
  { client },
);
if (dates.error) throw dates.error;
```

## Alphabetical groups

`getSeriesAlphabeticalGroups` accepts the flat `seriesSearch` body parameter and returns group counts:

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

const result = await getSeriesAlphabeticalGroups({ seriesSearch: {} }, { client });

if (result.error) {
  console.error('Could not load alphabetical groups', result.error);
} else {
  for (const group of result.data ?? []) {
    console.log(`${group.group}: ${group.count} series`);
  }
}
```

## Get series by ID

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

const result = await getSeriesById({ seriesId: 'series-123' }, { client });

if (result.error) {
  console.error('Series lookup failed', result.error);
} else if (result.data) {
  console.log(`Title: ${result.data.metadata.title}`);
  console.log(`Books: ${result.data.booksCount}`);
  console.log(`Status: ${result.data.metadata.status}`);
}
```

## Get series from a collection

`getSeriesByCollectionId` uses `id` for the collection identifier and exposes pagination as flat keys:

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

const result = await getSeriesByCollectionId(
  { id: 'collection-123', page: 0, size: 50 },
  { client },
);

if (result.error) {
  console.error('Could not load collection series', result.error);
} else {
  for (const series of result.data?.content ?? []) {
    console.log(series.metadata.title);
  }
}
```

`getSeriesByCollectionId` also supports flat filters such as `library_id`, `status`, `read_status`, `publisher`, `language`, `genre`, `tag`, `age_rating`, `release_year`, `deleted`, `complete`, and `author`, in addition to `unpaged`, `page`, and `size`. Use those filters instead of fetching every collection series and filtering locally.

## Navigate book siblings

```typescript theme={null}
import { getBookSiblingNext, getBookSiblingPrevious } from 'komga-sdk';

const nextResult = await getBookSiblingNext({ bookId: 'book-123' }, { client });
if (nextResult.error) {
  console.error('Could not load the next book', nextResult.error);
} else if (nextResult.data) {
  console.log(`Next: ${nextResult.data.metadata.title}`);
} else {
  console.log('This is the last book in the series');
}

const previousResult = await getBookSiblingPrevious({ bookId: 'book-123' }, { client });
if (previousResult.error) {
  console.error('Could not load the previous book', previousResult.error);
} else if (previousResult.data) {
  console.log(`Previous: ${previousResult.data.metadata.title}`);
} else {
  console.log('This is the first book in the series');
}
```

## Series metadata operations

### Update series metadata

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

const result = await updateSeriesMetadata(
  {
    seriesId: 'series-123',
    seriesMetadataUpdateDto: {
      title: 'New Series Title',
      summary: 'Updated description',
      status: 'ONGOING',
      genres: ['Action', 'Adventure'],
    },
  },
  { client },
);

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

### Refresh metadata

> [!WARNING]
>
> Requires **ADMIN** role or appropriate permissions.

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

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

### Analyze series

> [!WARNING]
>
> Requires **ADMIN** role.

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

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

## Common workflows

### Build a "New Releases" dashboard

```typescript theme={null}
import { getSeriesLatest, getSeriesReleaseDates } from 'komga-sdk';

async function getNewReleasesDashboard() {
  const [latest, releases] = await Promise.all([
    getSeriesLatest({ page: 0, size: 10 }, { client }),
    getSeriesReleaseDates(undefined, { client }),
  ]);

  if (latest.error) throw latest.error;
  if (releases.error) throw releases.error;

  return {
    recentlyAdded: latest.data?.content ?? [],
    releaseDates: releases.data ?? [],
  };
}
```

### A-Z navigation

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

async function buildAZNavigator() {
  const result = await getSeriesAlphabeticalGroups({ seriesSearch: {} }, { client });
  if (result.error) throw result.error;

  return (result.data ?? []).map((group) => ({
    letter: group.group,
    count: group.count,
  }));
}
```

### Series browser with pagination

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

class SeriesBrowser {
  private currentPage = 0;
  private readonly pageSize = 20;

  async loadPage(page: number) {
    this.currentPage = page;
    const result = await getSeries(
      {
        page,
        size: this.pageSize,
        sort: ['metadata.title,asc'],
        seriesSearch: {},
      },
      { client },
    );
    if (result.error) throw result.error;
    return result.data;
  }

  async getSeriesDetails(seriesId: string) {
    const result = await getSeriesById({ seriesId }, { client });
    if (result.error) throw result.error;
    return result.data;
  }

  nextPage() {
    return this.loadPage(this.currentPage + 1);
  }

  prevPage() {
    return this.currentPage > 0 ? this.loadPage(this.currentPage - 1) : undefined;
  }
}
```

### Reading order navigator

```typescript theme={null}
import { getBookSiblingNext, getBookSiblingPrevious } from 'komga-sdk';

class ReadingNavigator {
  constructor(private currentBookId: string) {}

  async next() {
    const result = await getBookSiblingNext({ bookId: this.currentBookId }, { client });
    if (result.error) throw result.error;
    if (!result.data) return null;

    this.currentBookId = result.data.id;
    return result.data;
  }

  async previous() {
    const result = await getBookSiblingPrevious({ bookId: this.currentBookId }, { client });
    if (result.error) throw result.error;
    if (!result.data) return null;

    this.currentBookId = result.data.id;
    return result.data;
  }
}
```

## Error handling

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

const result = await getSeriesById({ seriesId: 'series-123' }, { client });

if (result.error) {
  switch (result.response?.status) {
    case 404:
      console.error('Series not found');
      break;
    case 401:
      console.error('Not authenticated');
      break;
    default:
      console.error('Error fetching series', result.error);
  }
}
```

## Next steps

> **[Series](/guides/series)**
>
> Learn about generated series operations.

> **[Books](/guides/books)**
>
> Explore book operations and content access.

> **[Collections](/guides/collections)**
>
> Organize series into collections.

> **[Libraries](/guides/libraries)**
>
> Manage your library structure.


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