diff --git a/lib/config/types.ts b/lib/config/types.ts
index 7de52aecf0750e8f7f83ce69bd54091087921a87..04a02bc91b08c465c116c71cdd739918c07df53c 100644
--- a/lib/config/types.ts
+++ b/lib/config/types.ts
@@ -264,7 +264,7 @@ export interface RenovateConfig
 
 export interface CustomDatasourceConfig {
   defaultRegistryUrlTemplate?: string;
-  format?: 'json' | 'plain';
+  format?: 'json' | 'plain' | 'yaml';
   transformTemplates?: string[];
 }
 
diff --git a/lib/modules/datasource/custom/formats/yaml.ts b/lib/modules/datasource/custom/formats/yaml.ts
new file mode 100644
index 0000000000000000000000000000000000000000..baf67f10308008729e89d6ec839d6d86acec35c3
--- /dev/null
+++ b/lib/modules/datasource/custom/formats/yaml.ts
@@ -0,0 +1,9 @@
+import yaml from 'js-yaml';
+
+import type { Http } from '../../../../util/http';
+
+export async function fetch(http: Http, url: string): Promise<unknown> {
+  const response = await http.get(url);
+
+  return yaml.load(response.body);
+}
diff --git a/lib/modules/datasource/custom/index.spec.ts b/lib/modules/datasource/custom/index.spec.ts
index e6edfe1227c53a8aa78b76039064ed44651bde34..05eb3cb685bbf5b14a56384c3d55d9a317d3946a 100644
--- a/lib/modules/datasource/custom/index.spec.ts
+++ b/lib/modules/datasource/custom/index.spec.ts
@@ -1,3 +1,4 @@
+import { codeBlock } from 'common-tags';
 import { getPkgReleases } from '..';
 import * as httpMock from '../../../../test/http-mock';
 import { CustomDatasource } from './index';
@@ -198,6 +199,46 @@ describe('modules/datasource/custom/index', () => {
       expect(result).toBeNull();
     });
 
+    it('return releases for yaml API directly exposing in Renovate format', async () => {
+      const expected = {
+        releases: [
+          {
+            version: '1.0.0',
+          },
+          {
+            version: '2.0.0',
+          },
+          {
+            version: '3.0.0',
+          },
+        ],
+      };
+
+      const yaml = codeBlock`
+        releases:
+          - version: 1.0.0
+          - version: 2.0.0
+          - version: 3.0.0
+      `;
+
+      httpMock.scope('https://example.com').get('/v1').reply(200, yaml, {
+        'Content-Type': 'text/yaml',
+      });
+
+      const result = await getPkgReleases({
+        datasource: `${CustomDatasource.id}.foo`,
+        packageName: 'myPackage',
+        customDatasources: {
+          foo: {
+            defaultRegistryUrlTemplate: 'https://example.com/v1',
+            format: 'yaml',
+          },
+        },
+      });
+
+      expect(result).toEqual(expected);
+    });
+
     it('return release when templating registryUrl', async () => {
       const expected = {
         releases: [
diff --git a/lib/modules/datasource/custom/index.ts b/lib/modules/datasource/custom/index.ts
index 166e7958fe3dfbb75af22817eae7c40747d256b2..6d7167504e1b2169662eb30eae7f757ec1347dd7 100644
--- a/lib/modules/datasource/custom/index.ts
+++ b/lib/modules/datasource/custom/index.ts
@@ -4,6 +4,7 @@ import { logger } from '../../../logger';
 import { Datasource } from '../datasource';
 import type { GetReleasesConfig, ReleaseResult } from '../types';
 import { fetch as plainFetch } from './formats/plain';
+import { fetch as yamlFetch } from './formats/yaml';
 import { ReleaseResultZodSchema } from './schema';
 import { getCustomConfig } from './utils';
 
@@ -31,6 +32,9 @@ export class CustomDatasource extends Datasource {
         case 'plain':
           response = await plainFetch(this.http, defaultRegistryUrlTemplate);
           break;
+        case 'yaml':
+          response = await yamlFetch(this.http, defaultRegistryUrlTemplate);
+          break;
         case 'json':
           response = (await this.http.getJson(defaultRegistryUrlTemplate)).body;
       }
diff --git a/lib/modules/datasource/custom/readme.md b/lib/modules/datasource/custom/readme.md
index 288856418c300b87912f6f85fd9dba79a620d92c..0105766b3f51e41e555d0afaa53d3c7509c48581 100644
--- a/lib/modules/datasource/custom/readme.md
+++ b/lib/modules/datasource/custom/readme.md
@@ -118,6 +118,39 @@ When Renovate receives this response with the `plain` format, it will convert it
 
 After the conversion, any `jsonata` rules defined in the `transformTemplates` section will be applied as usual to further process the JSON data.
 
+### Yaml
+
+If `yaml` is used, response is parsed and converted into JSON for further processing.
+
+Suppose the body of the HTTP response is as follows:
+
+```yaml
+releases:
+  - version: 1.0.0
+  - version: 2.0.0
+  - version: 3.0.0
+```
+
+When Renovate receives this response with the `yaml` format, it will convert it into the following:
+
+```json
+{
+  "releases": [
+    {
+      "version": "1.0.0"
+    },
+    {
+      "version": "2.0.0"
+    },
+    {
+      "version": "3.0.0"
+    }
+  ]
+}
+```
+
+After the conversion, any `jsonata` rules defined in the `transformTemplates` section will be applied as usual to further process the JSON data.
+
 ## Examples
 
 ### K3s