diff --git a/lib/util/http/bitbucket.spec.ts b/lib/util/http/bitbucket.spec.ts
index ac7bb06e010322a3268dc18ed5fc807f2dc5443e..7f74909d1c8175e5ce052a353d33120206807d2e 100644
--- a/lib/util/http/bitbucket.spec.ts
+++ b/lib/util/http/bitbucket.spec.ts
@@ -55,4 +55,34 @@ describe('util/http/bitbucket', () => {
       statusCode: 200,
     });
   });
+
+  it('paginates', async () => {
+    httpMock
+      .scope(baseUrl)
+      .get('/some-url')
+      .reply(200, {
+        values: ['a'],
+        page: '1',
+        next: `${baseUrl}/some-url?page=2`,
+      })
+      .get('/some-url?page=2')
+      .reply(200, {
+        values: ['b', 'c'],
+        page: '2',
+        next: `${baseUrl}/some-url?page=3`,
+      })
+      .get('/some-url?page=3')
+      .reply(200, {
+        values: ['d'],
+        page: '3',
+      });
+    const res = await api.getJson('some-url', { paginate: true });
+    expect(res.body).toEqual({
+      page: '1',
+      pagelen: 4,
+      size: 4,
+      values: ['a', 'b', 'c', 'd'],
+      next: undefined,
+    });
+  });
 });
diff --git a/lib/util/http/bitbucket.ts b/lib/util/http/bitbucket.ts
index 628ef154cba1fed7270f17e9c4a4f5f0b62774a1..15bea99b04860ad2009006d1e7cf4a1836dafbdf 100644
--- a/lib/util/http/bitbucket.ts
+++ b/lib/util/http/bitbucket.ts
@@ -1,4 +1,7 @@
-import type { HttpOptions, HttpResponse, InternalHttpOptions } from './types';
+import is from '@sindresorhus/is';
+import type { PagedResult } from '../../modules/platform/bitbucket/types';
+import { parseUrl, resolveBaseUrl } from '../url';
+import type { HttpOptions, HttpResponse } from './types';
 import { Http } from '.';
 
 let baseUrl = 'https://api.bitbucket.org/';
@@ -7,16 +10,79 @@ export const setBaseUrl = (url: string): void => {
   baseUrl = url;
 };
 
-export class BitbucketHttp extends Http {
-  constructor(type = 'bitbucket', options?: HttpOptions) {
+export interface BitbucketHttpOptions extends HttpOptions {
+  paginate?: boolean;
+}
+
+export class BitbucketHttp extends Http<BitbucketHttpOptions> {
+  constructor(type = 'bitbucket', options?: BitbucketHttpOptions) {
     super(type, options);
   }
 
-  protected override request<T>(
-    url: string | URL,
-    options?: InternalHttpOptions
+  protected override async request<T>(
+    path: string,
+    options?: BitbucketHttpOptions
   ): Promise<HttpResponse<T>> {
     const opts = { baseUrl, ...options };
-    return super.request<T>(url, opts);
+
+    const result = await super.request<T>(path, opts);
+
+    if (opts.paginate && isPagedResult(result.body)) {
+      const resultBody = result.body as PagedResult<T>;
+
+      let nextPage = getPageFromURL(resultBody.next);
+
+      while (is.nonEmptyString(nextPage)) {
+        const nextPath = getNextPagePath(path, nextPage);
+
+        // istanbul ignore if
+        if (is.nullOrUndefined(nextPath)) {
+          break;
+        }
+
+        const nextResult = await super.request<PagedResult<T>>(
+          nextPath,
+          options
+        );
+
+        resultBody.values.push(...nextResult.body.values);
+
+        nextPage = getPageFromURL(nextResult.body?.next);
+      }
+
+      // Override other page-related attributes
+      resultBody.pagelen = resultBody.values.length;
+      resultBody.size = resultBody.values.length;
+      resultBody.next = undefined;
+    }
+
+    return result;
   }
 }
+
+function getPageFromURL(url: string | undefined): string | null {
+  const resolvedURL = parseUrl(url);
+
+  if (is.nullOrUndefined(resolvedURL)) {
+    return null;
+  }
+
+  return resolvedURL.searchParams.get('page');
+}
+
+function getNextPagePath(path: string, nextPage: string): string | null {
+  const resolvedURL = parseUrl(resolveBaseUrl(baseUrl, path));
+
+  // istanbul ignore if
+  if (is.nullOrUndefined(resolvedURL)) {
+    return null;
+  }
+
+  resolvedURL.searchParams.set('page', nextPage);
+
+  return resolvedURL.toString();
+}
+
+function isPagedResult(obj: any): obj is PagedResult {
+  return is.nonEmptyObject(obj) && Array.isArray(obj.values);
+}