diff --git a/.github/workflows/test-python.yml b/.github/workflows/test-python.yml
index 4f1ffd7..354c5ca 100644
--- a/.github/workflows/test-python.yml
+++ b/.github/workflows/test-python.yml
@@ -149,6 +149,35 @@ jobs:
     - name: Run simple code
       run: python -c 'import math; print(math.factorial(5))'
 
+  setup-prerelease-version:
+    name: Setup 3.12 ${{ matrix.os }}
+    runs-on: ${{ matrix.os }}
+    strategy:
+      fail-fast: false
+      matrix:
+        os: [macos-latest, windows-latest, ubuntu-latest]
+    steps:
+    - name: Checkout
+      uses: actions/checkout@v3
+
+    - name: setup-python 3.12
+      id: setup-python
+      uses: ./
+      with:
+        python-version: '3.12'
+        allow-prereleases: true
+
+    - name: Check python-path
+      run: ./__tests__/check-python-path.sh '${{ steps.setup-python.outputs.python-path }}'
+      shell: bash
+
+    - name: Validate version
+      run: ${{ startsWith(steps.setup-python.outputs.python-version, '3.12.') }}
+      shell: bash
+
+    - name: Run simple code
+      run: python -c 'import math; print(math.factorial(5))'
+
   setup-versions-noenv:
     name: Setup ${{ matrix.python }} ${{ matrix.os }} (noenv)
     runs-on: ${{ matrix.os }}
@@ -223,4 +252,4 @@ jobs:
             exit 1
           }
           $pythonVersion
-        shell: pwsh
\ No newline at end of file
+        shell: pwsh
diff --git a/README.md b/README.md
index 08619c6..5b6efbf 100644
--- a/README.md
+++ b/README.md
@@ -85,6 +85,7 @@ See examples of using `cache` and `cache-dependency-path` for `pipenv` and `poet
 - [Hosted tool cache](docs/advanced-usage.md#hosted-tool-cache) 
 - [Using `setup-python` with a self-hosted runner](docs/advanced-usage.md#using-setup-python-with-a-self-hosted-runner)
 - [Using `setup-python` on GHES](docs/advanced-usage.md#using-setup-python-on-ghes)
+- [Allow pre-releases](docs/advanced-usage.md#allow-pre-releases)
 
 ## License
 
diff --git a/__tests__/data/pypy.json b/__tests__/data/pypy.json
index c8889a3..fab9bb9 100644
--- a/__tests__/data/pypy.json
+++ b/__tests__/data/pypy.json
@@ -1,4 +1,94 @@
 [
+  {
+    "pypy_version": "7.3.8rc2",
+    "python_version": "3.8.12",
+    "stable": false,
+    "latest_pypy": false,
+    "date": "2022-02-11",
+    "files": [
+      {
+        "filename": "pypy3.8-v7.3.8rc2-linux32.tar.bz2",
+        "arch": "i686",
+        "platform": "linux",
+        "download_url": "https://test.download.python.org/pypy/pypy3.8-v7.3.8rc2-linux32.tar.bz2"
+      },
+      {
+        "filename": "pypy3.8-v7.3.8rc2-linux64.tar.bz2",
+        "arch": "x64",
+        "platform": "linux",
+        "download_url": "https://test.download.python.org/pypy/pypy3.8-v7.3.8rc2-linux64.tar.bz2"
+      },
+      {
+        "filename": "pypy3.8-v7.3.8rc2-darwin64.tar.bz2",
+        "arch": "x64",
+        "platform": "darwin",
+        "download_url": "https://test.download.python.org/pypy/pypy3.8-v7.3.8rc2-darwin64.tar.bz2"
+      },
+      {
+        "filename": "pypy3.8-v7.3.8rc2-s390x.tar.bz2",
+        "arch": "s390x",
+        "platform": "linux",
+        "download_url": "https://test.download.python.org/pypy/pypy3.8-v7.3.8rc2-s390x.tar.bz2"
+      },
+      {
+        "filename": "pypy3.8-v7.3.8rc2-win64.zip",
+        "arch": "x64",
+        "platform": "win64",
+        "download_url": "https://test.download.python.org/pypy/pypy3.8-v7.3.8rc2-win64.zip"
+      },
+      {
+        "filename": "pypy3.8-v7.3.8rc2-win32.zip",
+        "arch": "x86",
+        "platform": "win32",
+        "download_url": "https://test.download.python.org/pypy/pypy3.8-v7.3.8rc2-win32.zip"
+      }
+    ]
+  },
+  {
+    "pypy_version": "7.4.0rc1",
+    "python_version": "3.6.12",
+    "stable": false,
+    "latest_pypy": false,
+    "date": "2021-11-11",
+    "files": [
+      {
+        "filename": "pypy3.6-v7.4.0rc1-aarch64.tar.bz2",
+        "arch": "aarch64",
+        "platform": "linux",
+        "download_url": "https://test.download.python.org/pypy/pypy3.6-v7.4.0rc1-aarch64.tar.bz2"
+      },
+      {
+        "filename": "pypy3.6-v7.4.0rc1-linux32.tar.bz2",
+        "arch": "i686",
+        "platform": "linux",
+        "download_url": "https://test.download.python.org/pypy/pypy3.6-v7.4.0rc1-linux32.tar.bz2"
+      },
+      {
+        "filename": "pypy3.6-v7.4.0rc1-linux64.tar.bz2",
+        "arch": "x64",
+        "platform": "linux",
+        "download_url": "https://test.download.python.org/pypy/pypy3.6-v7.4.0rc1-linux64.tar.bz2"
+      },
+      {
+        "filename": "pypy3.6-v7.4.0rc1-darwin64.tar.bz2",
+        "arch": "x64",
+        "platform": "darwin",
+        "download_url": "https://test.download.python.org/pypy/pypy3.6-v7.4.0rc1-darwin64.tar.bz2"
+      },
+      {
+        "filename": "pypy3.6-v7.4.0rc1-win32.zip",
+        "arch": "x86",
+        "platform": "win32",
+        "download_url": "https://test.download.python.org/pypy/pypy3.6-v7.4.0rc1-win32.zip"
+      },
+      {
+        "filename": "pypy3.6-v7.4.0rc1-s390x.tar.bz2",
+        "arch": "s390x",
+        "platform": "linux",
+        "download_url": "https://test.download.python.org/pypy/pypy3.6-v7.4.0rc1-s390x.tar.bz2"
+      }
+    ]
+  },
   {
     "pypy_version": "7.3.3",
     "python_version": "3.6.12",
@@ -530,4 +620,4 @@
       }
     ]
   }
-]
\ No newline at end of file
+]
diff --git a/__tests__/data/versions-manifest.json b/__tests__/data/versions-manifest.json
index 2d23f98..083160f 100644
--- a/__tests__/data/versions-manifest.json
+++ b/__tests__/data/versions-manifest.json
@@ -1,4 +1,29 @@
 [
+    {
+      "version": "1.2.4-beta.2",
+      "stable": false,
+      "release_url": "https://github.com/actions/sometool/releases/tag/1.2.4-beta.2-20200402.5",
+      "files": [
+        {
+          "filename": "sometool-1.2.4-linux-x64.tar.gz",
+          "arch": "x64",
+          "platform": "linux",
+          "download_url": "https://github.com/actions/sometool/releases/tag/1.2.4-beta.2-20200402.5/sometool-1.2.4-linux-x64.tar.gz"
+        },
+        {
+          "filename": "sometool-1.2.4-darwin-x64.tar.gz",
+          "arch": "x64",
+          "platform": "darwin",
+          "download_url": "https://github.com/actions/sometool/releases/tag/1.2.4-beta.2-20200402.5/sometool-1.2.4-darwin-x64.tar.gz"
+        },
+        {
+          "filename": "sometool-1.2.4-win32-x64.tar.gz",
+          "arch": "x64",
+          "platform": "win32",
+          "download_url": "https://github.com/actions/sometool/releases/tag/1.2.4-beta.2-20200402.5/sometool-1.2.4-win32-x64.tar.gz"
+        }
+      ]
+    },
     {
       "version": "1.2.3",
       "stable": true,
@@ -25,28 +50,28 @@
       ]
     },
     {
-      "version": "1.2.3-beta.2",
+      "version": "1.1.0-beta.2",
       "stable": false,
-      "release_url": "https://github.com/actions/sometool/releases/tag/1.2.3-beta.2-20200402.5",
+      "release_url": "https://github.com/actions/sometool/releases/tag/1.1.0-beta.2-20200402.5",
       "files": [
         {
-          "filename": "sometool-1.2.3-linux-x64.tar.gz",
+          "filename": "sometool-1.1.0-linux-x64.tar.gz",
           "arch": "x64",
           "platform": "linux",
-          "download_url": "https://github.com/actions/sometool/releases/tag/1.2.3-beta.2-20200402.5/sometool-1.2.3-linux-x64.tar.gz"
+          "download_url": "https://github.com/actions/sometool/releases/tag/1.1.0-beta.2-20200402.5/sometool-1.1.0-linux-x64.tar.gz"
         },
         {
-          "filename": "sometool-1.2.3-darwin-x64.tar.gz",
+          "filename": "sometool-1.1.0-darwin-x64.tar.gz",
           "arch": "x64",
           "platform": "darwin",
-          "download_url": "https://github.com/actions/sometool/releases/tag/1.2.3-20200402.5/sometool-1.2.3-darwin-x64.tar.gz"
+          "download_url": "https://github.com/actions/sometool/releases/tag/1.1.0-beta.2-20200402.5/sometool-1.1.0-darwin-x64.tar.gz"
         },
         {
-          "filename": "sometool-1.2.3-win32-x64.tar.gz",
+          "filename": "sometool-1.1.0-win32-x64.tar.gz",
           "arch": "x64",
           "platform": "win32",
-          "download_url": "https://github.com/actions/sometool/releases/tag/1.2.3-20200402.5/sometool-1.2.3-win32-x64.tar.gz"
+          "download_url": "https://github.com/actions/sometool/releases/tag/1.1.0-beta.2-20200402.5/sometool-1.1.0-win32-x64.tar.gz"
         }
       ]
     }
-]
\ No newline at end of file
+]
diff --git a/__tests__/find-pypy.test.ts b/__tests__/find-pypy.test.ts
index 660f23d..66be1f1 100644
--- a/__tests__/find-pypy.test.ts
+++ b/__tests__/find-pypy.test.ts
@@ -273,7 +273,13 @@ describe('findPyPyVersion', () => {
 
   it('found PyPy in toolcache', async () => {
     await expect(
-      finder.findPyPyVersion('pypy-3.6-v7.3.x', architecture, true, false)
+      finder.findPyPyVersion(
+        'pypy-3.6-v7.3.x',
+        architecture,
+        true,
+        false,
+        false
+      )
     ).resolves.toEqual({
       resolvedPythonVersion: '3.6.12',
       resolvedPyPyVersion: '7.3.3'
@@ -291,13 +297,13 @@ describe('findPyPyVersion', () => {
 
   it('throw on invalid input format', async () => {
     await expect(
-      finder.findPyPyVersion('pypy3.7-v7.3.x', architecture, true, false)
+      finder.findPyPyVersion('pypy3.7-v7.3.x', architecture, true, false, false)
     ).rejects.toThrow();
   });
 
   it('throw on invalid input format pypy3.7-7.3.x', async () => {
     await expect(
-      finder.findPyPyVersion('pypy3.7-v7.3.x', architecture, true, false)
+      finder.findPyPyVersion('pypy3.7-v7.3.x', architecture, true, false, false)
     ).rejects.toThrow();
   });
 
@@ -309,7 +315,13 @@ describe('findPyPyVersion', () => {
     spyChmodSync = jest.spyOn(fs, 'chmodSync');
     spyChmodSync.mockImplementation(() => undefined);
     await expect(
-      finder.findPyPyVersion('pypy-3.7-v7.3.x', architecture, true, false)
+      finder.findPyPyVersion(
+        'pypy-3.7-v7.3.x',
+        architecture,
+        true,
+        false,
+        false
+      )
     ).resolves.toEqual({
       resolvedPythonVersion: '3.7.9',
       resolvedPyPyVersion: '7.3.3'
@@ -333,7 +345,13 @@ describe('findPyPyVersion', () => {
     spyChmodSync = jest.spyOn(fs, 'chmodSync');
     spyChmodSync.mockImplementation(() => undefined);
     await expect(
-      finder.findPyPyVersion('pypy-3.7-v7.3.x', architecture, false, false)
+      finder.findPyPyVersion(
+        'pypy-3.7-v7.3.x',
+        architecture,
+        false,
+        false,
+        false
+      )
     ).resolves.toEqual({
       resolvedPythonVersion: '3.7.9',
       resolvedPyPyVersion: '7.3.3'
@@ -344,7 +362,13 @@ describe('findPyPyVersion', () => {
 
   it('throw if release is not found', async () => {
     await expect(
-      finder.findPyPyVersion('pypy-3.7-v7.5.x', architecture, true, false)
+      finder.findPyPyVersion(
+        'pypy-3.7-v7.5.x',
+        architecture,
+        true,
+        false,
+        false
+      )
     ).rejects.toThrowError(
       `PyPy version 3.7 (v7.5.x) with arch ${architecture} not found`
     );
@@ -352,7 +376,13 @@ describe('findPyPyVersion', () => {
 
   it('check-latest enabled version found and used from toolcache', async () => {
     await expect(
-      finder.findPyPyVersion('pypy-3.6-v7.3.x', architecture, false, true)
+      finder.findPyPyVersion(
+        'pypy-3.6-v7.3.x',
+        architecture,
+        false,
+        true,
+        false
+      )
     ).resolves.toEqual({
       resolvedPythonVersion: '3.6.12',
       resolvedPyPyVersion: '7.3.3'
@@ -371,7 +401,13 @@ describe('findPyPyVersion', () => {
     spyChmodSync = jest.spyOn(fs, 'chmodSync');
     spyChmodSync.mockImplementation(() => undefined);
     await expect(
-      finder.findPyPyVersion('pypy-3.7-v7.3.x', architecture, false, true)
+      finder.findPyPyVersion(
+        'pypy-3.7-v7.3.x',
+        architecture,
+        false,
+        true,
+        false
+      )
     ).resolves.toEqual({
       resolvedPythonVersion: '3.7.9',
       resolvedPyPyVersion: '7.3.3'
@@ -391,7 +427,13 @@ describe('findPyPyVersion', () => {
       return pypyPath;
     });
     await expect(
-      finder.findPyPyVersion('pypy-3.8-v7.3.x', architecture, false, true)
+      finder.findPyPyVersion(
+        'pypy-3.8-v7.3.x',
+        architecture,
+        false,
+        true,
+        false
+      )
     ).resolves.toEqual({
       resolvedPythonVersion: '3.8.8',
       resolvedPyPyVersion: '7.3.3'
@@ -401,4 +443,22 @@ describe('findPyPyVersion', () => {
       'Failed to resolve PyPy v7.3.x with Python (3.8) from manifest'
     );
   });
+
+  it('found and install successfully, pre-release fallback', async () => {
+    spyCacheDir = jest.spyOn(tc, 'cacheDir');
+    spyCacheDir.mockImplementation(() =>
+      path.join(toolDir, 'PyPy', '3.8.12', architecture)
+    );
+    spyChmodSync = jest.spyOn(fs, 'chmodSync');
+    spyChmodSync.mockImplementation(() => undefined);
+    await expect(
+      finder.findPyPyVersion('pypy3.8', architecture, false, false, false)
+    ).rejects.toThrowError();
+    await expect(
+      finder.findPyPyVersion('pypy3.8', architecture, false, false, true)
+    ).resolves.toEqual({
+      resolvedPythonVersion: '3.8.12',
+      resolvedPyPyVersion: '7.3.8rc2'
+    });
+  });
 });
diff --git a/__tests__/finder.test.ts b/__tests__/finder.test.ts
index d2fe775..b26c709 100644
--- a/__tests__/finder.test.ts
+++ b/__tests__/finder.test.ts
@@ -56,7 +56,7 @@ describe('Finder tests', () => {
     await io.mkdirP(pythonDir);
     fs.writeFileSync(`${pythonDir}.complete`, 'hello');
     // This will throw if it doesn't find it in the cache and in the manifest (because no such version exists)
-    await finder.useCpythonVersion('3.x', 'x64', true, false);
+    await finder.useCpythonVersion('3.x', 'x64', true, false, false);
     expect(spyCoreAddPath).toHaveBeenCalled();
     expect(spyCoreExportVariable).toHaveBeenCalledWith(
       'pythonLocation',
@@ -73,7 +73,7 @@ describe('Finder tests', () => {
     await io.mkdirP(pythonDir);
     fs.writeFileSync(`${pythonDir}.complete`, 'hello');
     // This will throw if it doesn't find it in the cache and in the manifest (because no such version exists)
-    await finder.useCpythonVersion('3.x', 'x64', false, false);
+    await finder.useCpythonVersion('3.x', 'x64', false, false, false);
     expect(spyCoreAddPath).not.toHaveBeenCalled();
     expect(spyCoreExportVariable).not.toHaveBeenCalled();
   });
@@ -95,7 +95,12 @@ describe('Finder tests', () => {
       fs.writeFileSync(`${pythonDir}.complete`, 'hello');
     });
     // This will throw if it doesn't find it in the cache and in the manifest (because no such version exists)
-    await finder.useCpythonVersion('1.2.3', 'x64', true, false);
+    await expect(
+      finder.useCpythonVersion('1.2.3', 'x64', true, false, false)
+    ).resolves.toEqual({
+      impl: 'CPython',
+      version: '1.2.3'
+    });
     expect(spyCoreAddPath).toHaveBeenCalled();
     expect(spyCoreExportVariable).toHaveBeenCalledWith(
       'pythonLocation',
@@ -122,14 +127,19 @@ describe('Finder tests', () => {
       const pythonDir: string = path.join(
         toolDir,
         'Python',
-        '1.2.3-beta.2',
+        '1.2.4-beta.2',
         'x64'
       );
       await io.mkdirP(pythonDir);
       fs.writeFileSync(`${pythonDir}.complete`, 'hello');
     });
     // This will throw if it doesn't find it in the manifest (because no such version exists)
-    await finder.useCpythonVersion('1.2.3-beta.2', 'x64', false, false);
+    await expect(
+      finder.useCpythonVersion('1.2.4-beta.2', 'x64', false, false, false)
+    ).resolves.toEqual({
+      impl: 'CPython',
+      version: '1.2.4-beta.2'
+    });
   });
 
   it('Check-latest true, finds the latest version in the manifest', async () => {
@@ -176,7 +186,7 @@ describe('Finder tests', () => {
 
     fs.writeFileSync(`${pythonDir}.complete`, 'hello');
     // This will throw if it doesn't find it in the cache and in the manifest (because no such version exists)
-    await finder.useCpythonVersion('1.2', 'x64', true, true);
+    await finder.useCpythonVersion('1.2', 'x64', true, true, false);
 
     expect(infoSpy).toHaveBeenCalledWith("Resolved as '1.2.3'");
     expect(infoSpy).toHaveBeenCalledWith(
@@ -187,7 +197,7 @@ describe('Finder tests', () => {
     );
     expect(installSpy).toHaveBeenCalled();
     expect(addPathSpy).toHaveBeenCalledWith(expPath);
-    await finder.useCpythonVersion('1.2.3-beta.2', 'x64', false, true);
+    await finder.useCpythonVersion('1.2.4-beta.2', 'x64', false, true, false);
     expect(spyCoreAddPath).toHaveBeenCalled();
     expect(spyCoreExportVariable).toHaveBeenCalledWith(
       'pythonLocation',
@@ -199,11 +209,67 @@ describe('Finder tests', () => {
     );
   });
 
+  it('Finds stable Python version if it is not installed, but exists in the manifest, skipping newer pre-release', async () => {
+    const findSpy: jest.SpyInstance = jest.spyOn(tc, 'getManifestFromRepo');
+    findSpy.mockImplementation(() => <tc.IToolRelease[]>manifestData);
+
+    const installSpy: jest.SpyInstance = jest.spyOn(
+      installer,
+      'installCpythonFromRelease'
+    );
+    installSpy.mockImplementation(async () => {
+      const pythonDir: string = path.join(toolDir, 'Python', '1.2.3', 'x64');
+      await io.mkdirP(pythonDir);
+      fs.writeFileSync(`${pythonDir}.complete`, 'hello');
+    });
+    // This will throw if it doesn't find it in the cache and in the manifest (because no such version exists)
+    await expect(
+      finder.useCpythonVersion('1.2', 'x64', false, false, false)
+    ).resolves.toEqual({
+      impl: 'CPython',
+      version: '1.2.3'
+    });
+  });
+
+  it('Finds Python version if it is not installed, but exists in the manifest, pre-release fallback', async () => {
+    const findSpy: jest.SpyInstance = jest.spyOn(tc, 'getManifestFromRepo');
+    findSpy.mockImplementation(() => <tc.IToolRelease[]>manifestData);
+
+    const installSpy: jest.SpyInstance = jest.spyOn(
+      installer,
+      'installCpythonFromRelease'
+    );
+    installSpy.mockImplementation(async () => {
+      const pythonDir: string = path.join(
+        toolDir,
+        'Python',
+        '1.1.0-beta.2',
+        'x64'
+      );
+      await io.mkdirP(pythonDir);
+      fs.writeFileSync(`${pythonDir}.complete`, 'hello');
+    });
+    // This will throw if it doesn't find it in the cache and in the manifest (because no such version exists)
+    await expect(
+      finder.useCpythonVersion('1.1', 'x64', false, false, false)
+    ).rejects.toThrowError();
+    await expect(
+      finder.useCpythonVersion('1.1', 'x64', false, false, true)
+    ).resolves.toEqual({
+      impl: 'CPython',
+      version: '1.1.0-beta.2'
+    });
+    // Check 1.1.0 version specifier does not fallback to '1.1.0-beta.2'
+    await expect(
+      finder.useCpythonVersion('1.1.0', 'x64', false, false, true)
+    ).rejects.toThrowError();
+  });
+
   it('Errors if Python is not installed', async () => {
     // This will throw if it doesn't find it in the cache and in the manifest (because no such version exists)
     let thrown = false;
     try {
-      await finder.useCpythonVersion('3.300000', 'x64', true, false);
+      await finder.useCpythonVersion('3.300000', 'x64', true, false, false);
     } catch {
       thrown = true;
     }
diff --git a/__tests__/install-pypy.test.ts b/__tests__/install-pypy.test.ts
index ae7fb4a..d08daa2 100644
--- a/__tests__/install-pypy.test.ts
+++ b/__tests__/install-pypy.test.ts
@@ -51,6 +51,12 @@ describe('findRelease', () => {
     platform: process.platform,
     download_url: `https://test.download.python.org/pypy/pypy3.6-v7.3.3-${extensionName}`
   };
+  const filesRC1: IPyPyManifestAsset = {
+    filename: `pypy3.6-v7.4.0rc1-${extensionName}`,
+    arch: architecture,
+    platform: process.platform,
+    download_url: `https://test.download.python.org/pypy/pypy3.6-v7.4.0rc1-${extensionName}`
+  };
 
   let getBooleanInputSpy: jest.SpyInstance;
   let warningSpy: jest.SpyInstance;
@@ -72,7 +78,13 @@ describe('findRelease', () => {
     const pythonVersion = '3.6';
     const pypyVersion = '7.3.7';
     expect(
-      installer.findRelease(releases, pythonVersion, pypyVersion, architecture)
+      installer.findRelease(
+        releases,
+        pythonVersion,
+        pypyVersion,
+        architecture,
+        false
+      )
     ).toEqual(null);
   });
 
@@ -80,7 +92,13 @@ describe('findRelease', () => {
     const pythonVersion = '3.6';
     const pypyVersion = '7.3.3';
     expect(
-      installer.findRelease(releases, pythonVersion, pypyVersion, architecture)
+      installer.findRelease(
+        releases,
+        pythonVersion,
+        pypyVersion,
+        architecture,
+        false
+      )
     ).toEqual({
       foundAsset: files,
       resolvedPythonVersion: '3.6.12',
@@ -92,7 +110,13 @@ describe('findRelease', () => {
     const pythonVersion = '3.6';
     const pypyVersion = '7.x';
     expect(
-      installer.findRelease(releases, pythonVersion, pypyVersion, architecture)
+      installer.findRelease(
+        releases,
+        pythonVersion,
+        pypyVersion,
+        architecture,
+        false
+      )
     ).toEqual({
       foundAsset: files,
       resolvedPythonVersion: '3.6.12',
@@ -104,7 +128,13 @@ describe('findRelease', () => {
     const pythonVersion = '3.7';
     const pypyVersion = installer.pypyVersionToSemantic('7.3.3rc2');
     expect(
-      installer.findRelease(releases, pythonVersion, pypyVersion, architecture)
+      installer.findRelease(
+        releases,
+        pythonVersion,
+        pypyVersion,
+        architecture,
+        false
+      )
     ).toEqual({
       foundAsset: {
         filename: `test${extension}`,
@@ -121,7 +151,13 @@ describe('findRelease', () => {
     const pythonVersion = '3.6';
     const pypyVersion = 'x';
     expect(
-      installer.findRelease(releases, pythonVersion, pypyVersion, architecture)
+      installer.findRelease(
+        releases,
+        pythonVersion,
+        pypyVersion,
+        architecture,
+        false
+      )
     ).toEqual({
       foundAsset: files,
       resolvedPythonVersion: '3.6.12',
@@ -129,12 +165,45 @@ describe('findRelease', () => {
     });
   });
 
+  it('Python version and PyPy version matches semver (pre-release)', () => {
+    const pythonVersion = '3.6';
+    const pypyVersion = '7.4.x';
+    expect(
+      installer.findRelease(
+        releases,
+        pythonVersion,
+        pypyVersion,
+        architecture,
+        false
+      )
+    ).toBeNull();
+    expect(
+      installer.findRelease(
+        releases,
+        pythonVersion,
+        pypyVersion,
+        architecture,
+        true
+      )
+    ).toEqual({
+      foundAsset: filesRC1,
+      resolvedPythonVersion: '3.6.12',
+      resolvedPyPyVersion: '7.4.0rc1'
+    });
+  });
+
   it('Nightly release is found', () => {
     const pythonVersion = '3.6';
     const pypyVersion = 'nightly';
     const filename = IS_WINDOWS ? 'filename.zip' : 'filename.tar.bz2';
     expect(
-      installer.findRelease(releases, pythonVersion, pypyVersion, architecture)
+      installer.findRelease(
+        releases,
+        pythonVersion,
+        pypyVersion,
+        architecture,
+        false
+      )
     ).toEqual({
       foundAsset: {
         filename: filename,
@@ -224,7 +293,7 @@ describe('installPyPy', () => {
 
   it('throw if release is not found', async () => {
     await expect(
-      installer.installPyPy('7.3.3', '3.6.17', architecture, undefined)
+      installer.installPyPy('7.3.3', '3.6.17', architecture, false, undefined)
     ).rejects.toThrowError(
       `PyPy version 3.6.17 (7.3.3) with arch ${architecture} not found`
     );
@@ -244,7 +313,7 @@ describe('installPyPy', () => {
     spyChmodSync.mockImplementation(() => undefined);
 
     await expect(
-      installer.installPyPy('7.3.x', '3.6.12', architecture, undefined)
+      installer.installPyPy('7.x', '3.6.12', architecture, false, undefined)
     ).resolves.toEqual({
       installDir: path.join(toolDir, 'PyPy', '3.6.12', architecture),
       resolvedPythonVersion: '3.6.12',
@@ -257,4 +326,31 @@ describe('installPyPy', () => {
     expect(spyCacheDir).toHaveBeenCalled();
     expect(spyExec).toHaveBeenCalled();
   });
+
+  it('found and install PyPy, pre-release fallback', async () => {
+    spyCacheDir = jest.spyOn(tc, 'cacheDir');
+    spyCacheDir.mockImplementation(() =>
+      path.join(toolDir, 'PyPy', '3.6.12', architecture)
+    );
+
+    spyChmodSync = jest.spyOn(fs, 'chmodSync');
+    spyChmodSync.mockImplementation(() => undefined);
+
+    await expect(
+      installer.installPyPy('7.4.x', '3.6.12', architecture, false, undefined)
+    ).rejects.toThrowError();
+    await expect(
+      installer.installPyPy('7.4.x', '3.6.12', architecture, true, undefined)
+    ).resolves.toEqual({
+      installDir: path.join(toolDir, 'PyPy', '3.6.12', architecture),
+      resolvedPythonVersion: '3.6.12',
+      resolvedPyPyVersion: '7.4.0rc1'
+    });
+
+    expect(spyHttpClient).toHaveBeenCalled();
+    expect(spyDownloadTool).toHaveBeenCalled();
+    expect(spyExistsSync).toHaveBeenCalled();
+    expect(spyCacheDir).toHaveBeenCalled();
+    expect(spyExec).toHaveBeenCalled();
+  });
 });
diff --git a/action.yml b/action.yml
index b8bb06b..3a6531c 100644
--- a/action.yml
+++ b/action.yml
@@ -23,6 +23,9 @@ inputs:
   update-environment:
     description: "Set this option if you want the action to update environment variables."
     default: true
+  allow-prereleases:
+    description: "When 'true', a version range passed to 'python-version' input will match prerelease versions if no GA versions are found. Only 'x.y' version range is supported for CPython."
+    default: false
 outputs:
   python-version:
     description: "The installed Python or PyPy version. Useful when given a version range as input."
diff --git a/dist/setup/index.js b/dist/setup/index.js
index 4cf2767..18cc54b 100644
--- a/dist/setup/index.js
+++ b/dist/setup/index.js
@@ -66237,7 +66237,7 @@ const utils_1 = __nccwpck_require__(1314);
 const semver = __importStar(__nccwpck_require__(1383));
 const core = __importStar(__nccwpck_require__(2186));
 const tc = __importStar(__nccwpck_require__(7784));
-function findPyPyVersion(versionSpec, architecture, updateEnvironment, checkLatest) {
+function findPyPyVersion(versionSpec, architecture, updateEnvironment, checkLatest, allowPreReleases) {
     return __awaiter(this, void 0, void 0, function* () {
         let resolvedPyPyVersion = '';
         let resolvedPythonVersion = '';
@@ -66247,7 +66247,7 @@ function findPyPyVersion(versionSpec, architecture, updateEnvironment, checkLate
         if (checkLatest) {
             releases = yield pypyInstall.getAvailablePyPyVersions();
             if (releases && releases.length > 0) {
-                const releaseData = pypyInstall.findRelease(releases, pypyVersionSpec.pythonVersion, pypyVersionSpec.pypyVersion, architecture);
+                const releaseData = pypyInstall.findRelease(releases, pypyVersionSpec.pythonVersion, pypyVersionSpec.pypyVersion, architecture, false);
                 if (releaseData) {
                     core.info(`Resolved as PyPy ${releaseData.resolvedPyPyVersion} with Python (${releaseData.resolvedPythonVersion})`);
                     pypyVersionSpec.pythonVersion = releaseData.resolvedPythonVersion;
@@ -66264,7 +66264,7 @@ function findPyPyVersion(versionSpec, architecture, updateEnvironment, checkLate
                 installDir,
                 resolvedPythonVersion,
                 resolvedPyPyVersion
-            } = yield pypyInstall.installPyPy(pypyVersionSpec.pypyVersion, pypyVersionSpec.pythonVersion, architecture, releases));
+            } = yield pypyInstall.installPyPy(pypyVersionSpec.pypyVersion, pypyVersionSpec.pythonVersion, architecture, allowPreReleases, releases));
         }
         const pipDir = utils_1.IS_WINDOWS ? 'Scripts' : 'bin';
         const _binDir = path.join(installDir, pipDir);
@@ -66414,12 +66414,12 @@ function binDir(installDir) {
         return path.join(installDir, 'bin');
     }
 }
-function useCpythonVersion(version, architecture, updateEnvironment, checkLatest) {
+function useCpythonVersion(version, architecture, updateEnvironment, checkLatest, allowPreReleases) {
     var _a;
     return __awaiter(this, void 0, void 0, function* () {
         let manifest = null;
         const desugaredVersionSpec = desugarDevVersion(version);
-        let semanticVersionSpec = pythonVersionToSemantic(desugaredVersionSpec);
+        let semanticVersionSpec = pythonVersionToSemantic(desugaredVersionSpec, allowPreReleases);
         core.debug(`Semantic version spec of ${version} is ${semanticVersionSpec}`);
         if (checkLatest) {
             manifest = yield installer.getManifest();
@@ -66510,10 +66510,17 @@ function versionFromPath(installDir) {
  * Python's prelease versions look like `3.7.0b2`.
  * This is the one part of Python versioning that does not look like semantic versioning, which specifies `3.7.0-b2`.
  * If the version spec contains prerelease versions, we need to convert them to the semantic version equivalent.
+ *
+ * For easier use of the action, we also map 'x.y' to allow pre-release before 'x.y.0' release if allowPreReleases is true
  */
-function pythonVersionToSemantic(versionSpec) {
+function pythonVersionToSemantic(versionSpec, allowPreReleases) {
     const prereleaseVersion = /(\d+\.\d+\.\d+)((?:a|b|rc)\d*)/g;
-    return versionSpec.replace(prereleaseVersion, '$1-$2');
+    const majorMinor = /^(\d+)\.(\d+)$/;
+    let result = versionSpec.replace(prereleaseVersion, '$1-$2');
+    if (allowPreReleases) {
+        result = result.replace(majorMinor, '~$1.$2.0-0');
+    }
+    return result;
 }
 exports.pythonVersionToSemantic = pythonVersionToSemantic;
 
@@ -66558,6 +66565,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
 };
 Object.defineProperty(exports, "__esModule", ({ value: true }));
 exports.findAssetForMacOrLinux = exports.findAssetForWindows = exports.isArchPresentForMacOrLinux = exports.isArchPresentForWindows = exports.pypyVersionToSemantic = exports.getPyPyBinaryPath = exports.findRelease = exports.getAvailablePyPyVersions = exports.installPyPy = void 0;
+const os = __importStar(__nccwpck_require__(2037));
 const path = __importStar(__nccwpck_require__(1017));
 const core = __importStar(__nccwpck_require__(2186));
 const tc = __importStar(__nccwpck_require__(7784));
@@ -66566,14 +66574,22 @@ const httpm = __importStar(__nccwpck_require__(9925));
 const exec = __importStar(__nccwpck_require__(1514));
 const fs_1 = __importDefault(__nccwpck_require__(7147));
 const utils_1 = __nccwpck_require__(1314);
-function installPyPy(pypyVersion, pythonVersion, architecture, releases) {
+function installPyPy(pypyVersion, pythonVersion, architecture, allowPreReleases, releases) {
     return __awaiter(this, void 0, void 0, function* () {
         let downloadDir;
         releases = releases !== null && releases !== void 0 ? releases : (yield getAvailablePyPyVersions());
         if (!releases || releases.length === 0) {
             throw new Error('No release was found in PyPy version.json');
         }
-        const releaseData = findRelease(releases, pythonVersion, pypyVersion, architecture);
+        let releaseData = findRelease(releases, pythonVersion, pypyVersion, architecture, false);
+        if (allowPreReleases && (!releaseData || !releaseData.foundAsset)) {
+            // check for pre-release
+            core.info([
+                `Stable PyPy version ${pythonVersion} (${pypyVersion}) with arch ${architecture} not found`,
+                `Trying pre-release versions`
+            ].join(os.EOL));
+            releaseData = findRelease(releases, pythonVersion, pypyVersion, architecture, true);
+        }
         if (!releaseData || !releaseData.foundAsset) {
             throw new Error(`PyPy version ${pythonVersion} (${pypyVersion}) with arch ${architecture} not found`);
         }
@@ -66656,12 +66672,13 @@ function installPip(pythonLocation) {
         yield exec.exec(`${pythonLocation}/python -m pip install --ignore-installed pip`);
     });
 }
-function findRelease(releases, pythonVersion, pypyVersion, architecture) {
+function findRelease(releases, pythonVersion, pypyVersion, architecture, includePrerelease) {
+    const options = { includePrerelease: includePrerelease };
     const filterReleases = releases.filter(item => {
         const isPythonVersionSatisfied = semver.satisfies(semver.coerce(item.python_version), pythonVersion);
         const isPyPyNightly = utils_1.isNightlyKeyword(pypyVersion) && utils_1.isNightlyKeyword(item.pypy_version);
         const isPyPyVersionSatisfied = isPyPyNightly ||
-            semver.satisfies(pypyVersionToSemantic(item.pypy_version), pypyVersion);
+            semver.satisfies(pypyVersionToSemantic(item.pypy_version), pypyVersion, options);
         const isArchPresent = item.files &&
             (utils_1.IS_WINDOWS
                 ? isArchPresentForWindows(item, architecture)
@@ -66948,6 +66965,7 @@ function run() {
         try {
             const versions = resolveVersionInput();
             const checkLatest = core.getBooleanInput('check-latest');
+            const allowPreReleases = core.getBooleanInput('allow-prereleases');
             if (versions.length) {
                 let pythonVersion = '';
                 const arch = core.getInput('architecture') || os.arch();
@@ -66955,12 +66973,12 @@ function run() {
                 core.startGroup('Installed versions');
                 for (const version of versions) {
                     if (isPyPyVersion(version)) {
-                        const installed = yield finderPyPy.findPyPyVersion(version, arch, updateEnvironment, checkLatest);
+                        const installed = yield finderPyPy.findPyPyVersion(version, arch, updateEnvironment, checkLatest, allowPreReleases);
                         pythonVersion = `${installed.resolvedPyPyVersion}-${installed.resolvedPythonVersion}`;
                         core.info(`Successfully set up PyPy ${installed.resolvedPyPyVersion} with Python (${installed.resolvedPythonVersion})`);
                     }
                     else {
-                        const installed = yield finder.useCpythonVersion(version, arch, updateEnvironment, checkLatest);
+                        const installed = yield finder.useCpythonVersion(version, arch, updateEnvironment, checkLatest, allowPreReleases);
                         pythonVersion = installed.version;
                         core.info(`Successfully set up ${installed.impl} (${pythonVersion})`);
                     }
diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md
index d752898..50b3c81 100644
--- a/docs/advanced-usage.md
+++ b/docs/advanced-usage.md
@@ -20,6 +20,7 @@
     - [Linux](advanced-usage.md#linux)
     - [macOS](advanced-usage.md#macos)
 - [Using `setup-python` on GHES](advanced-usage.md#using-setup-python-on-ghes)
+- [Allow pre-releases](advanced-usage.md#allow-pre-releases)
 
 ## Using the `python-version` input
 
@@ -568,3 +569,31 @@ Requests should now be authenticated. To verify that you are getting the higher
 
 ### No access to github.com
 If the runner is not able to access github.com, any Python versions requested during a workflow run must come from the runner's tool cache. See "[Setting up the tool cache on self-hosted runners without internet access](https://docs.github.com/en/enterprise-server@3.2/admin/github-actions/managing-access-to-actions-from-githubcom/setting-up-the-tool-cache-on-self-hosted-runners-without-internet-access)" for more information.
+
+
+## Allow pre-releases
+
+The `allow-prereleases` flag defaults to `false`.
+If `allow-prereleases` is set to `true`, the action will allow falling back to pre-release versions of Python when a matching GA version of Python is not available.
+This allows for example to simplify reuse of `python-version` as an input of nox for pre-releases of Python by not requiring manipulation of the `3.y-dev` specifier.
+For CPython, `allow-prereleases` will only have effect for `x.y` version range (e.g. `3.12`).
+Let's say that python 3.12 is not generally available, the following workflow will fallback to the most recent pre-release of python 3.12:
+```yaml
+jobs:
+  test:
+    name: ${{ matrix.os }} / ${{ matrix.python_version }}
+    runs-on: ${{ matrix.os }}-latest
+    strategy:
+      fail-fast: false
+      matrix:
+        os: [Ubuntu, Windows, macOS]
+        python_version: ["3.11", "3.12"]
+
+    steps:
+      - uses: actions/checkout@v3
+      - uses: actions/setup-python@v4
+        with:
+          python-version: "${{ matrix.python_version }}"
+      - run: pipx run nox --error-on-missing-interpreters -s tests-${{ matrix.python_version }}
+```
+
diff --git a/src/find-pypy.ts b/src/find-pypy.ts
index 20b9821..44bfd5a 100644
--- a/src/find-pypy.ts
+++ b/src/find-pypy.ts
@@ -23,7 +23,8 @@ export async function findPyPyVersion(
   versionSpec: string,
   architecture: string,
   updateEnvironment: boolean,
-  checkLatest: boolean
+  checkLatest: boolean,
+  allowPreReleases: boolean
 ): Promise<{resolvedPyPyVersion: string; resolvedPythonVersion: string}> {
   let resolvedPyPyVersion = '';
   let resolvedPythonVersion = '';
@@ -39,7 +40,8 @@ export async function findPyPyVersion(
         releases,
         pypyVersionSpec.pythonVersion,
         pypyVersionSpec.pypyVersion,
-        architecture
+        architecture,
+        false
       );
 
       if (releaseData) {
@@ -71,6 +73,7 @@ export async function findPyPyVersion(
       pypyVersionSpec.pypyVersion,
       pypyVersionSpec.pythonVersion,
       architecture,
+      allowPreReleases,
       releases
     ));
   }
diff --git a/src/find-python.ts b/src/find-python.ts
index c156d2a..5a760d6 100644
--- a/src/find-python.ts
+++ b/src/find-python.ts
@@ -34,11 +34,15 @@ export async function useCpythonVersion(
   version: string,
   architecture: string,
   updateEnvironment: boolean,
-  checkLatest: boolean
+  checkLatest: boolean,
+  allowPreReleases: boolean
 ): Promise<InstalledVersion> {
   let manifest: tc.IToolRelease[] | null = null;
   const desugaredVersionSpec = desugarDevVersion(version);
-  let semanticVersionSpec = pythonVersionToSemantic(desugaredVersionSpec);
+  let semanticVersionSpec = pythonVersionToSemantic(
+    desugaredVersionSpec,
+    allowPreReleases
+  );
   core.debug(`Semantic version spec of ${version} is ${semanticVersionSpec}`);
 
   if (checkLatest) {
@@ -178,8 +182,18 @@ interface InstalledVersion {
  * Python's prelease versions look like `3.7.0b2`.
  * This is the one part of Python versioning that does not look like semantic versioning, which specifies `3.7.0-b2`.
  * If the version spec contains prerelease versions, we need to convert them to the semantic version equivalent.
+ *
+ * For easier use of the action, we also map 'x.y' to allow pre-release before 'x.y.0' release if allowPreReleases is true
  */
-export function pythonVersionToSemantic(versionSpec: string) {
+export function pythonVersionToSemantic(
+  versionSpec: string,
+  allowPreReleases: boolean
+) {
   const prereleaseVersion = /(\d+\.\d+\.\d+)((?:a|b|rc)\d*)/g;
-  return versionSpec.replace(prereleaseVersion, '$1-$2');
+  const majorMinor = /^(\d+)\.(\d+)$/;
+  let result = versionSpec.replace(prereleaseVersion, '$1-$2');
+  if (allowPreReleases) {
+    result = result.replace(majorMinor, '~$1.$2.0-0');
+  }
+  return result;
 }
diff --git a/src/install-pypy.ts b/src/install-pypy.ts
index f7df9c5..9327d62 100644
--- a/src/install-pypy.ts
+++ b/src/install-pypy.ts
@@ -1,3 +1,4 @@
+import * as os from 'os';
 import * as path from 'path';
 import * as core from '@actions/core';
 import * as tc from '@actions/tool-cache';
@@ -19,6 +20,7 @@ export async function installPyPy(
   pypyVersion: string,
   pythonVersion: string,
   architecture: string,
+  allowPreReleases: boolean,
   releases: IPyPyManifestRelease[] | undefined
 ) {
   let downloadDir;
@@ -29,13 +31,31 @@ export async function installPyPy(
     throw new Error('No release was found in PyPy version.json');
   }
 
-  const releaseData = findRelease(
+  let releaseData = findRelease(
     releases,
     pythonVersion,
     pypyVersion,
-    architecture
+    architecture,
+    false
   );
 
+  if (allowPreReleases && (!releaseData || !releaseData.foundAsset)) {
+    // check for pre-release
+    core.info(
+      [
+        `Stable PyPy version ${pythonVersion} (${pypyVersion}) with arch ${architecture} not found`,
+        `Trying pre-release versions`
+      ].join(os.EOL)
+    );
+    releaseData = findRelease(
+      releases,
+      pythonVersion,
+      pypyVersion,
+      architecture,
+      true
+    );
+  }
+
   if (!releaseData || !releaseData.foundAsset) {
     throw new Error(
       `PyPy version ${pythonVersion} (${pypyVersion}) with arch ${architecture} not found`
@@ -162,8 +182,10 @@ export function findRelease(
   releases: IPyPyManifestRelease[],
   pythonVersion: string,
   pypyVersion: string,
-  architecture: string
+  architecture: string,
+  includePrerelease: boolean
 ) {
+  const options = {includePrerelease: includePrerelease};
   const filterReleases = releases.filter(item => {
     const isPythonVersionSatisfied = semver.satisfies(
       semver.coerce(item.python_version)!,
@@ -173,7 +195,11 @@ export function findRelease(
       isNightlyKeyword(pypyVersion) && isNightlyKeyword(item.pypy_version);
     const isPyPyVersionSatisfied =
       isPyPyNightly ||
-      semver.satisfies(pypyVersionToSemantic(item.pypy_version), pypyVersion);
+      semver.satisfies(
+        pypyVersionToSemantic(item.pypy_version),
+        pypyVersion,
+        options
+      );
     const isArchPresent =
       item.files &&
       (IS_WINDOWS
diff --git a/src/setup-python.ts b/src/setup-python.ts
index 0089b40..3be958a 100644
--- a/src/setup-python.ts
+++ b/src/setup-python.ts
@@ -77,6 +77,7 @@ async function run() {
   try {
     const versions = resolveVersionInput();
     const checkLatest = core.getBooleanInput('check-latest');
+    const allowPreReleases = core.getBooleanInput('allow-prereleases');
 
     if (versions.length) {
       let pythonVersion = '';
@@ -89,7 +90,8 @@ async function run() {
             version,
             arch,
             updateEnvironment,
-            checkLatest
+            checkLatest,
+            allowPreReleases
           );
           pythonVersion = `${installed.resolvedPyPyVersion}-${installed.resolvedPythonVersion}`;
           core.info(
@@ -100,7 +102,8 @@ async function run() {
             version,
             arch,
             updateEnvironment,
-            checkLatest
+            checkLatest,
+            allowPreReleases
           );
           pythonVersion = installed.version;
           core.info(`Successfully set up ${installed.impl} (${pythonVersion})`);