diff --git a/lib/modules/datasource/hex/index.ts b/lib/modules/datasource/hex/index.ts
index 8e45bd491ab6c3d1de5853502b521b31d2fde568..e6ee7d927cc364b73b1d40c10005a9236aee67de 100644
--- a/lib/modules/datasource/hex/index.ts
+++ b/lib/modules/datasource/hex/index.ts
@@ -1,10 +1,9 @@
 import { logger } from '../../../logger';
 import { cache } from '../../../util/cache/package/decorator';
-import type { HttpResponse } from '../../../util/http/types';
 import * as hexVersioning from '../../versioning/hex';
 import { Datasource } from '../datasource';
 import type { GetReleasesConfig, ReleaseResult } from '../types';
-import type { HexRelease } from './types';
+import { HexRelease } from './schema';
 
 export class HexDatasource extends Datasource {
   static readonly id = 'hex';
@@ -43,44 +42,15 @@ export class HexDatasource extends Datasource {
       : '';
     const hexUrl = `${registryUrl}api/${organizationUrlPrefix}packages/${hexPackageName}`;
 
-    let response: HttpResponse<HexRelease>;
-    try {
-      response = await this.http.getJson<HexRelease>(hexUrl);
-    } catch (err) {
-      this.handleGenericErrors(err);
-    }
-
-    const hexRelease: HexRelease = response.body;
-
-    if (!hexRelease) {
-      logger.warn({ datasource: 'hex', packageName }, `Invalid response body`);
-      return null;
-    }
-
-    const { releases = [], html_url: homepage, meta } = hexRelease;
+    const { val: result, err } = await this.http
+      .getJsonSafe(hexUrl, HexRelease)
+      .onError((err) => {
+        logger.warn({ datasource: 'hex', packageName, err }, `Error fetching ${hexUrl}`); // prettier-ignore
+      })
+      .unwrap();
 
-    if (releases.length === 0) {
-      logger.debug(`No versions found for ${hexPackageName} (${hexUrl})`); // prettier-ignore
-      return null;
-    }
-
-    const result: ReleaseResult = {
-      releases: releases.map(({ version, inserted_at }) =>
-        inserted_at
-          ? {
-              version,
-              releaseTimestamp: inserted_at,
-            }
-          : { version },
-      ),
-    };
-
-    if (homepage) {
-      result.homepage = homepage;
-    }
-
-    if (meta?.links?.Github) {
-      result.sourceUrl = meta?.links?.Github;
+    if (err) {
+      this.handleGenericErrors(err);
     }
 
     return result;
diff --git a/lib/modules/datasource/hex/schema.ts b/lib/modules/datasource/hex/schema.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b1015ae664d0656811cbc575e9ce692a63e1a6e1
--- /dev/null
+++ b/lib/modules/datasource/hex/schema.ts
@@ -0,0 +1,47 @@
+import { z } from 'zod';
+import { LooseArray } from '../../../util/schema-utils';
+import type { Release, ReleaseResult } from '../types';
+
+export const HexRelease = z
+  .object({
+    html_url: z.string().optional(),
+    meta: z
+      .object({
+        links: z.object({
+          Github: z.string(),
+        }),
+      })
+      .nullable()
+      .catch(null),
+    releases: LooseArray(
+      z.object({
+        version: z.string(),
+        inserted_at: z.string().optional(),
+      }),
+    ).refine((releases) => releases.length > 0, 'No releases found'),
+  })
+  .transform((hexResponse): ReleaseResult => {
+    const releases: Release[] = hexResponse.releases.map(
+      ({ version, inserted_at: releaseTimestamp }): Release => {
+        const release: Release = { version };
+
+        if (releaseTimestamp) {
+          release.releaseTimestamp = releaseTimestamp;
+        }
+
+        return release;
+      },
+    );
+
+    const releaseResult: ReleaseResult = { releases };
+
+    if (hexResponse.html_url) {
+      releaseResult.homepage = hexResponse.html_url;
+    }
+
+    if (hexResponse.meta?.links?.Github) {
+      releaseResult.sourceUrl = hexResponse.meta.links.Github;
+    }
+
+    return releaseResult;
+  });
diff --git a/lib/modules/datasource/hex/types.ts b/lib/modules/datasource/hex/types.ts
deleted file mode 100644
index bf7eadc7976560f9f372dc3a8c83550765fecd7e..0000000000000000000000000000000000000000
--- a/lib/modules/datasource/hex/types.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-export interface HexRelease {
-  html_url: string;
-  meta?: { links?: Record<string, string> };
-  releases?: {
-    version: string;
-    inserted_at?: string;
-  }[];
-}