diff --git a/docs/usage.rst b/docs/usage.rst
index 86e3434..79b4c5b 100644
--- a/docs/usage.rst
+++ b/docs/usage.rst
@@ -587,6 +587,17 @@ Check `MetaCPAN `_ for updates.
cpan
The name used on CPAN, e.g. ``YAML``.
+Check CRAN
+~~~~~~~~~~
+::
+
+ source = "cran"
+
+Check `CRAN `_ for updates.
+
+cran
+ The name used on CRAN, e.g. ``xml2``.
+
Check Packagist
~~~~~~~~~~~~~~~
::
diff --git a/nvchecker_source/cran.py b/nvchecker_source/cran.py
new file mode 100644
index 0000000..53061ba
--- /dev/null
+++ b/nvchecker_source/cran.py
@@ -0,0 +1,26 @@
+# MIT licensed
+# Copyright (c) 2022 Pekka Ristola , et al.
+
+from nvchecker.api import session, GetVersionError
+
+CRAN_URL = 'https://cran.r-project.org/package=%s/DESCRIPTION'
+VERSION_FIELD = 'Version: '
+
+async def request(pkg):
+ url = CRAN_URL % pkg
+ res = await session.get(url)
+ return res.body.decode('utf-8', errors='ignore')
+
+async def get_version(name, conf, *, cache, **kwargs):
+ package = conf.get('cran', name)
+
+ desc = await cache.get(package, request)
+
+ for line in desc.splitlines():
+ if line.startswith(VERSION_FIELD):
+ version = line[len(VERSION_FIELD):]
+ break
+ else:
+ raise GetVersionError('Invalid DESCRIPTION file')
+
+ return version
diff --git a/tests/test_cran.py b/tests/test_cran.py
new file mode 100644
index 0000000..c3328f1
--- /dev/null
+++ b/tests/test_cran.py
@@ -0,0 +1,10 @@
+# MIT licensed
+# Copyright (c) 2022 Pekka Ristola , et al.
+
+import pytest
+pytestmark = [pytest.mark.asyncio, pytest.mark.needs_net]
+
+async def test_cran(get_version):
+ assert await get_version("xml2", {
+ "source": "cran",
+ }) == "1.3.3"