diff --git a/src/api/ApiHooks.ts b/src/api/ApiHooks.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b05dc36f929ec5f6e375a0b9216b75d25d8987c2
--- /dev/null
+++ b/src/api/ApiHooks.ts
@@ -0,0 +1,98 @@
+import {Content} from "./models/Content";
+import {useQuery} from "react-query";
+import {fetchJson} from "../util/fetchJson";
+import {Instalment} from "./models/Instalment";
+import {useApiEndpoint} from "./ApiEndpointContext";
+import {ContentMeta} from "./models/dto/ContentMeta";
+import {Genre} from "./models/Genre";
+
+export function useShowEpisodes(
+    item: Content | undefined
+): [Instalment[], boolean, unknown] {
+    const apiEndpoint = useApiEndpoint();
+
+    const {data, isLoading, error} = useQuery(
+        ["episodes", item?.ids?.uuid],
+        () => item?.kind === "show"
+            ? fetchJson<Instalment[]>(
+                apiEndpoint,
+                `api/v1/content/${item.ids.uuid}/episodes`,
+                {
+                    method: "GET"
+                }
+            )
+            : []
+    )
+
+    return [data || [], isLoading, error];
+}
+
+export function useSingleContent(
+    id: string
+): [ContentMeta | null, boolean, unknown] {
+    const apiEndpoint = useApiEndpoint();
+
+    const {data, isLoading, error} = useQuery(
+        ["media", id],
+        () => fetchJson<ContentMeta>(
+            apiEndpoint,
+            `api/v1/content/${id}`,
+            {
+                method: "GET"
+            }
+        )
+    )
+
+    return [data || null, isLoading, error];
+}
+
+export function useAllContent(): [Content[], boolean, unknown] {
+    const apiEndpoint = useApiEndpoint();
+
+    const {data, isLoading, error} = useQuery(
+        "media",
+        () => fetchJson<Content[]>(
+            apiEndpoint,
+            `api/v1/content`,
+            {
+                method: "GET"
+            }
+        )
+    );
+
+    return [data || [], isLoading, error];
+}
+
+export function useGenres(): [Genre[], boolean, unknown] {
+    const apiEndpoint = useApiEndpoint();
+
+    const {data, isLoading, error} = useQuery(
+        "media",
+        () => fetchJson<Genre[]>(
+            apiEndpoint,
+            `api/v1/genres`,
+            {
+                method: "GET"
+            }
+        )
+    );
+
+    return [data || [], isLoading, error];
+}
+
+export function useGenreContent(genreId: string): [Content[], boolean, unknown] {
+    const apiEndpoint = useApiEndpoint();
+
+    const {data, isLoading, error} = useQuery(
+        "media",
+        () => fetchJson<Content[]>(
+            apiEndpoint,
+            `api/v1/genres/${genreId}`,
+            {
+                method: "GET"
+            }
+        )
+    );
+
+    return [data || [], isLoading, error];
+}
diff --git a/src/api/hooks.ts b/src/api/hooks.ts
deleted file mode 100644
index 074f081cf326a5798da140080e036755f9c9bf04..0000000000000000000000000000000000000000
--- a/src/api/hooks.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import {Content} from "./models/Content";
-import {useQuery} from "react-query";
-import {getContent, listEpisodes} from "./queries";
-import {Instalment} from "./models/Instalment";
-import {useApiEndpoint} from "./ApiEndpointContext";
-import {ContentMeta} from "./models/dto/ContentMeta";
-
-export function useEpisodes(item: Content | undefined): [Instalment[], boolean, unknown] {
-    const apiEndpoint = useApiEndpoint();
-
-    const {data, isLoading, error} = useQuery(
-        ["episodes", item?.ids?.uuid],
-        () => item?.kind === "show"
-            ? listEpisodes(apiEndpoint, item.ids.uuid)
-            : []
-    )
-
-    return [data || [], isLoading, error];
-}
-
-export function useContent(id: string): [ContentMeta | null, boolean, unknown] {
-    const apiEndpoint = useApiEndpoint();
-
-    const {data, isLoading, error} = useQuery(
-        ["media", id],
-        () => getContent(apiEndpoint, id)
-    )
-
-    return [data || null, isLoading, error];
-}
diff --git a/src/api/queries.ts b/src/api/queries.ts
deleted file mode 100644
index d7ff6f01595c760515189fab238aaf83519f5e3f..0000000000000000000000000000000000000000
--- a/src/api/queries.ts
+++ /dev/null
@@ -1,53 +0,0 @@
-import {Genre} from "./models/Genre";
-import {GenreWithContent} from "./models/dto/GenreWithContent";
-import {Content} from "./models/Content";
-import {ContentMeta} from "./models/dto/ContentMeta";
-import {Instalment} from "./models/Instalment";
-import {RequestErrorKind} from "../util/request/RequestErrorKind";
-import {RequestError} from "../util/request/RequestError";
-
-async function request<T>(
-    baseUrl: URL,
-    path: string,
-    headers?: RequestInit
-): Promise<T> {
-    const url = new URL(path, baseUrl).toString();
-    const response = await fetch(url, headers);
-    if (response.ok) {
-        return await response.json();
-    } else if (response.status in RequestErrorKind) {
-        throw new RequestError(response.statusText, response.status)
-    } else {
-        throw new RequestError(response.statusText, RequestErrorKind.UnknownError)
-    }
-}
-
-export async function listGenres(baseUrl: URL): Promise<Genre[]> {
-    return await request(baseUrl, `api/v1/genres`, {
-        method: "GET"
-    });
-}
-
-export async function getGenre(baseUrl: URL, genreId: string): Promise<GenreWithContent> {
-    return await request(baseUrl, `api/v1/genres/${genreId}`, {
-        method: "GET"
-    });
-}
-
-export async function listContent(baseUrl: URL): Promise<Content[]> {
-    return await request(baseUrl, `api/v1/content`, {
-        method: "GET"
-    });
-}
-
-export async function getContent(baseUrl: URL, contentId: string): Promise<ContentMeta> {
-    return await request(baseUrl, `api/v1/content/${contentId}`, {
-        method: "GET"
-    });
-}
-
-export async function listEpisodes(baseUrl: URL, contentId: string): Promise<Instalment[]> {
-    return await request(baseUrl, `api/v1/content/${contentId}/episodes`, {
-        method: "GET"
-    });
-}
diff --git a/src/routes/ContentRoute.tsx b/src/routes/ContentRoute.tsx
index 5a9ec8b86d96d1da36d34e39382b967182cf75cb..23d9665f95a9fdea789d8a9bf6b2b1153116d004 100644
--- a/src/routes/ContentRoute.tsx
+++ b/src/routes/ContentRoute.tsx
@@ -1,12 +1,12 @@
 import {PropsWithChildren} from "react";
 import {useParams} from "react-router";
 import {CurrentContentProvider} from "../util/CurrentContentContext";
-import {useContent} from "../api/hooks";
+import {useSingleContent} from "../api/ApiHooks";
 
 export function ContentRoute(props: PropsWithChildren<{}>) {
     const {children} = props;
     const {contentId} = useParams<{ contentId: string }>();
-    const [meta/*, isLoading, error*/] = useContent(contentId);
+    const [meta/*, isLoading, error*/] = useSingleContent(contentId);
 
     return (
         <CurrentContentProvider value={meta}>
diff --git a/src/routes/main/MainPage.tsx b/src/routes/main/MainPage.tsx
index 752224f8e033d317c3db3f3ac2a3c8898fc264a6..ef2804e9de5b7b0f3b14968e52bc3f1dc40b9a5e 100644
--- a/src/routes/main/MainPage.tsx
+++ b/src/routes/main/MainPage.tsx
@@ -1,25 +1,23 @@
-import {useApiEndpoint} from "../../api/ApiEndpointContext";
 import {Content, getLocalizedName} from "../../api/models/Content";
 import {sortLexicallyAsc} from "../../util/sort/sortLexically";
-import {useQuery} from "react-query";
-import {listContent} from "../../api/queries";
 import {MediaEntry} from "./MediaEntry";
 import {useLocale} from "../../util/locale/LocalizedContext";
+import {useAllContent} from "../../api/ApiHooks";
 
 export function MainPage() {
-    const apiEndpoint = useApiEndpoint();
     const locale = useLocale();
-    const {data/*, isLoading, isError*/} = useQuery(
-        "media",
-        () => listContent(apiEndpoint)
-    );
-    const media = data || [];
+
+    const [media/*, isLoading, error*/] = useAllContent();
 
     return (
         <div>
             {media
                 .sort(sortLexicallyAsc(item => getLocalizedName(item, locale)?.name || ""))
-                .map((item: Content) => (<MediaEntry item={item}/>))}
+                .map((item: Content) => (
+                    <MediaEntry
+                        key={item.ids.uuid}
+                        item={item}/>
+                ))}
         </div>
     );
 }
diff --git a/src/routes/main/MediaEntry.tsx b/src/routes/main/MediaEntry.tsx
index 042417c385837461b238ed5aee8b05436970cb13..125472b155a3566b0e6943b2b7e09fb8739cebfe 100644
--- a/src/routes/main/MediaEntry.tsx
+++ b/src/routes/main/MediaEntry.tsx
@@ -3,7 +3,7 @@ import {Fragment} from "react";
 import {Link} from "react-router-dom";
 import {createUseStyles} from "react-jss";
 import {useLocale} from "../../util/locale/LocalizedContext";
-import {useEpisodes} from "../../api/hooks";
+import {useShowEpisodes} from "../../api/ApiHooks";
 import {MediaEpisode} from "./MediaEpisode";
 
 export interface Props {
@@ -16,7 +16,7 @@ export function MediaEntry(
     const locale = useLocale();
     const classes = useStyles();
 
-    const [episodes/*, episodesLoading*/, episodesError] = useEpisodes(item);
+    const [episodes/*, episodesLoading*/, episodesError] = useShowEpisodes(item);
 
     const title = getLocalizedName(item, locale);
     const description = getLocalizedDescription(item, locale);
@@ -25,7 +25,7 @@ export function MediaEntry(
     const backdrop = item.images.find(it => it.kind === "backdrop");
 
     return (
-        <div key={item.ids.uuid} className={classes.movie}>
+        <div className={classes.movie}>
             <h1>{title?.name}</h1>
             <p><strong>{rating?.certification}</strong></p>
             <p><strong>{description?.tagline}</strong></p>
@@ -38,11 +38,14 @@ export function MediaEntry(
             {item.kind === "show" && (
                 <Fragment>
                     {episodesError ? (
-                        <p>{""+episodesError}</p>
+                        <p>{"" + episodesError}</p>
                     ) : (
                         <ul>
                             {episodes.map(episode =>
-                                <MediaEpisode item={episode} />)}
+                                <MediaEpisode
+                                    key={episode.content.ids.uuid}
+                                    item={episode}
+                                />)}
                         </ul>
                     )}
                 </Fragment>
diff --git a/src/routes/main/MediaEpisode.tsx b/src/routes/main/MediaEpisode.tsx
index 498b80a95240094f572736b779cfc5b7da4cfc7a..ec815e17fc238380bf509f2e7301faf0b96f0120 100644
--- a/src/routes/main/MediaEpisode.tsx
+++ b/src/routes/main/MediaEpisode.tsx
@@ -14,7 +14,7 @@ export function MediaEpisode({item, disabled}: Props) {
     const episodeDescription = getLocalizedDescription(item.content, locale);
 
     return (
-        <li key={item.content.ids.uuid}>
+        <li>
             <p>
                 <strong>S{item.season}E{item.episode}</strong> – {item.airDate}
             </p>
diff --git a/src/routes/player/Player.tsx b/src/routes/player/Player.tsx
index b529f79f3dc45eff2f022606eca222e29d6f5496..aac33f4e10c32c71abd2cbe5f532d4b512a64aa7 100644
--- a/src/routes/player/Player.tsx
+++ b/src/routes/player/Player.tsx
@@ -17,7 +17,7 @@ import {Subtitle} from "../../api/models/Subtitle";
 import {MousePosition} from "../../util/mouse/MousePosition";
 import {VideoProvider} from "./video/VideoContext";
 import {SubtitleRenderer} from "./subtitles/SubtitleRenderer";
-import {useEpisodes} from "../../api/hooks";
+import {useShowEpisodes} from "../../api/ApiHooks";
 import {MediaEpisode} from "../main/MediaEpisode";
 import {usePaused} from "../../util/media/usePaused";
 
@@ -33,7 +33,7 @@ export function Player(
     const classes = useStyles();
     const locale = useLocale();
 
-    const [relatedEpisodes/*, relatedEpisodesLoading*/, relatedEpisodesError] = useEpisodes(instalment?.content);
+    const [relatedEpisodes/*, relatedEpisodesLoading*/, relatedEpisodesError] = useShowEpisodes(instalment?.content);
 
     const name = getLocalizedName(content, locale);
     const description = getLocalizedDescription(content, locale);
diff --git a/src/util/fetchJson.ts b/src/util/fetchJson.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f42e348178b8a22f82cadb7d7e6a815b02a8ea32
--- /dev/null
+++ b/src/util/fetchJson.ts
@@ -0,0 +1,18 @@
+import {RequestErrorKind} from "./request/RequestErrorKind";
+import {RequestError} from "./request/RequestError";
+
+export async function fetchJson<T>(
+    baseUrl: URL,
+    path: string,
+    options?: RequestInit
+): Promise<T> {
+    const url = new URL(path, baseUrl).toString();
+    const response = await fetch(url, options);
+    if (response.ok) {
+        return await response.json();
+    } else if (response.status in RequestErrorKind) {
+        throw new RequestError(response.statusText, response.status)
+    } else {
+        throw new RequestError(response.statusText, RequestErrorKind.UnknownError)
+    }
+}