diff --git a/pypistats/__init__.py b/pypistats/__init__.py
index 9ebc7e6..79cbc13 100644
--- a/pypistats/__init__.py
+++ b/pypistats/__init__.py
@@ -4,8 +4,8 @@
 Python interface to PyPI Stats API
 https://pypistats.org/api
 """
-import pytablewriter
 import requests
+from pytablewriter import Align, MarkdownTableWriter
 
 from . import version
 
@@ -47,12 +47,13 @@ def pypi_stats_api(
         return res
 
     # These only for table
+    data = res["data"]
     if sort:
-        res["data"] = _sort(res["data"])
+        data = _sort(data)
 
-    res["data"] = _grand_total(res["data"])
+    data = _percent(data)
+    data = _grand_total(data)
 
-    data = res["data"]
     return _tabulate(data)
 
 
@@ -121,10 +122,25 @@ def _grand_total(data):
     return data
 
 
+def _percent(data):
+    """Add a percent column"""
+
+    # Only for lists of dicts, not a single dict
+    if isinstance(data, dict):
+        return data
+
+    grand_total = sum(row["downloads"] for row in data)
+
+    for row in data:
+        row["percent"] = "{:.2%}".format(row["downloads"] / grand_total)
+
+    return data
+
+
 def _tabulate(data):
     """Return data in table"""
 
-    writer = pytablewriter.MarkdownTableWriter()
+    writer = MarkdownTableWriter()
     writer.margin = 1
 
     if isinstance(data, dict):
@@ -139,6 +155,13 @@ def _tabulate(data):
     header_list.remove("downloads")
     writer.header_list = header_list
 
+    # Custom alignment
+    writer.align_list = [Align.AUTO] * len(header_list)
+    try:
+        writer.align_list[header_list.index("percent")] = Align.RIGHT
+    except ValueError:
+        pass
+
     return writer.dumps()
 
 

Add percent column to tables
Put it before the downloads column.
diff --git a/tests/test_pypistats.py b/tests/test_pypistats.py
index 1e3f8fe..feae931 100644
--- a/tests/test_pypistats.py
+++ b/tests/test_pypistats.py
@@ -127,7 +127,7 @@ class TestPypiStats(unittest.TestCase):
 
     def test__tabulate(self):
         # Arrange
-        data = SAMPLE_DATA
+        data = copy.deepcopy(SAMPLE_DATA)
         expected_output = """
 | category |    date    | downloads |
 |----------|------------|----------:|
@@ -151,7 +151,7 @@ class TestPypiStats(unittest.TestCase):
 
     def test__sort(self):
         # Arrange
-        data = SAMPLE_DATA
+        data = copy.deepcopy(SAMPLE_DATA)
         expected_output = [
             {"category": "2.7", "date": "2018-08-15", "downloads": 63749},
             {"category": "3.6", "date": "2018-08-15", "downloads": 35274},
@@ -225,3 +225,34 @@ class TestPypiStats(unittest.TestCase):
 
         # Assert
         self.assertEqual(output, data)
+
+    def test__percent(self):
+        # Arrange
+        data = [
+            {"category": "2.7", "downloads": 63749},
+            {"category": "3.6", "downloads": 35274},
+            {"category": "2.6", "downloads": 51},
+            {"category": "3.2", "downloads": 2},
+        ]
+        expected_output = [
+            {"category": "2.7", "percent": "64.34%", "downloads": 63749},
+            {"category": "3.6", "percent": "35.60%", "downloads": 35274},
+            {"category": "2.6", "percent": "0.05%", "downloads": 51},
+            {"category": "3.2", "percent": "0.00%", "downloads": 2},
+        ]
+
+        # Act
+        output = pypistats._percent(data)
+
+        # Assert
+        self.assertEqual(output, expected_output)
+
+    def test__percent_recent(self):
+        # Arrange
+        data = {"last_day": 123002, "last_month": 3254221, "last_week": 761649}
+
+        # Act
+        output = pypistats._percent(data)
+
+        # Assert
+        self.assertEqual(output, data)

pip install -e .[dev]
parse_log_pytest
pytest
pytest pytest-cov flake8
3.9
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
certifi==2025.1.31
chardet==5.2.0
charset-normalizer==3.4.1
coverage==7.8.0
DataProperty==1.1.0
exceptiongroup @ file:///croot/exceptiongroup_1706031385326/work
flake8==7.2.0
idna==3.10
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
mbstrdecoder==1.1.4
mccabe==0.7.0
packaging @ file:///croot/packaging_1734472117206/work
pathvalidate==3.2.3
pluggy @ file:///croot/pluggy_1733169602837/work
pycodestyle==2.13.0
pyflakes==3.3.2
-e git+https://github.com/hugovk/pypistats.git@d36199f422c751e4dc9cf13bb409a94142121430#egg=pypistats
pytablewriter==1.2.1
pytest @ file:///croot/pytest_1738938843180/work
pytest-cov==6.0.0
python-dateutil==2.9.0.post0
pytz==2025.2
requests==2.32.3
six==1.17.0
tabledata==1.3.4
tcolorpy==0.1.7
tomli @ file:///opt/conda/conda-bld/tomli_1657175507142/work
typepy==1.3.4
urllib3==2.3.0

name: pypistats
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - exceptiongroup=1.2.0=py39h06a4308_0
  - iniconfig=1.1.1=pyhd3eb1b0_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - packaging=24.2=py39h06a4308_0
  - pip=25.0=py39h06a4308_0
  - pluggy=1.5.0=py39h06a4308_0
  - pytest=8.3.4=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tomli=2.0.1=py39h06a4308_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - certifi==2025.1.31
      - chardet==5.2.0
      - charset-normalizer==3.4.1
      - coverage==7.8.0
      - dataproperty==1.1.0
      - flake8==7.2.0
      - idna==3.10
      - mbstrdecoder==1.1.4
      - mccabe==0.7.0
      - pathvalidate==3.2.3
      - pycodestyle==2.13.0
      - pyflakes==3.3.2
      - pytablewriter==1.2.1
      - pytest-cov==6.0.0
      - python-dateutil==2.9.0.post0
      - pytz==2025.2
      - requests==2.32.3
      - six==1.17.0
      - tabledata==1.3.4
      - tcolorpy==0.1.7
      - typepy==1.3.4
      - urllib3==2.3.0
prefix: /opt/conda/envs/pypistats

tests/test_pypistats.py::TestPypiStats::test__percent tests/test_pypistats.py::TestPypiStats::test__percent_recent
tests/test_pypistats.py::TestPypiStats::test__tabulate
tests/test_pypistats.py::TestPypiStats::test__filter_end_date tests/test_pypistats.py::TestPypiStats::test__filter_no_filters_no_change tests/test_pypistats.py::TestPypiStats::test__filter_start_and_end_date tests/test_pypistats.py::TestPypiStats::test__filter_start_date tests/test_pypistats.py::TestPypiStats::test__grand_total tests/test_pypistats.py::TestPypiStats::test__grand_total_recent tests/test_pypistats.py::TestPypiStats::test__paramify_bool tests/test_pypistats.py::TestPypiStats::test__paramify_float tests/test_pypistats.py::TestPypiStats::test__paramify_int tests/test_pypistats.py::TestPypiStats::test__paramify_none tests/test_pypistats.py::TestPypiStats::test__paramify_string tests/test_pypistats.py::TestPypiStats::test__sort tests/test_pypistats.py::TestPypiStats::test__sort_recent tests/test_pypistats.py::TestPypiStats::test__total tests/test_pypistats.py::TestPypiStats::test__total_recent
pypistats/__init__.py
pypistats/__init__.py