diff --git a/README.rst b/README.rst
index 5e9b0eb..1a26c1f 100644
--- a/README.rst
+++ b/README.rst
@@ -307,6 +307,39 @@ Downloads between two YYYY-MM-DD dates
     | -------------- |
     |      9,572,911 |
 
+Downloads between two YYYY-MM dates
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+- A yyyy-mm ``--start-date`` defaults to the first day of the month
+- A yyyy-mm ``--end-date`` defaults to the last day of the month
+
+.. code-block:: console
+
+    $ pypinfo --start-date 2018-04 --end-date 2018-04 setuptools
+    Served from cache: True
+    Data processed: 0.00 B
+    Data billed: 0.00 B
+    Estimated cost: $0.00
+
+    | download_count |
+    | -------------- |
+    |      9,572,911 |
+
+Downloads for a single YYYY-MM month
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: console
+
+    $ pypinfo --month 2018-04 setuptools
+    Served from cache: True
+    Data processed: 0.00 B
+    Data billed: 0.00 B
+    Estimated cost: $0.00
+
+    | download_count |
+    | -------------- |
+    |      9,572,911 |
+
 Percentage of Python 3 downloads of the top 100 projects in the past year
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
@@ -346,11 +379,22 @@ Changelog
 
 Important changes are emphasized.
 
+Unreleased
+^^^^^^^^^^
+
+- Allow yyyy-mm[-dd] ``--start-date`` and ``--end-date``:
+
+  - A yyyy-mm ``--start-date`` defaults to the first day of the month
+  - A yyyy-mm ``--end-date`` defaults to the last day of the month
+
+- Add ``--month`` as a shortcut to ``--start-date`` and ``--end-date``
+  for a single yyyy-mm month
+
 15.0.0
 ^^^^^^
 
 - Allow yyyy-mm-dd dates
-- Add --all option, default to only showing downloads via pip
+- Add ``--all`` option, default to only showing downloads via pip
 - Add download total row
 
 14.0.0
diff --git a/pypinfo/cli.py b/pypinfo/cli.py
index 13a1607..7aeea47 100644
--- a/pypinfo/cli.py
+++ b/pypinfo/cli.py
@@ -10,6 +10,7 @@ from pypinfo.core import (
     create_client,
     create_config,
     format_json,
+    month_ends,
     parse_query_result,
     tabulate,
 )
@@ -77,8 +78,9 @@ TO_CENTS = Decimal('0.00')
 @click.option('--timeout', '-t', type=int, default=120000, help='Milliseconds. Default: 120000 (2 minutes)')
 @click.option('--limit', '-l', help='Maximum number of query results. Default: 10')
 @click.option('--days', '-d', help='Number of days in the past to include. Default: 30')
-@click.option('--start-date', '-sd', help='Must be negative or YYYY-MM-DD. Default: -31')
-@click.option('--end-date', '-ed', help='Must be negative or YYYY-MM-DD. Default: -1')
+@click.option('--start-date', '-sd', help='Must be negative or YYYY-MM[-DD]. Default: -31')
+@click.option('--end-date', '-ed', help='Must be negative or YYYY-MM[-DD]. Default: -1')
+@click.option('--month', '-m', help='Shortcut for -sd & -ed for a single YYYY-MM month.')
 @click.option('--where', '-w', help='WHERE conditional. Default: file.project = "project"')
 @click.option('--order', '-o', help='Field to order by. Default: download_count')
 @click.option('--all', 'all_installers', is_flag=True, help='Show downloads by all installers, not only pip.')
@@ -99,6 +101,7 @@ def pypinfo(
     days,
     start_date,
     end_date,
+    month,
     where,
     order,
     all_installers,
@@ -132,6 +135,9 @@ def pypinfo(
         order_name = order.name
         parsed_fields.insert(0, order)
 
+    if month:
+        start_date, end_date = month_ends(month)
+
     built_query = build_query(
         project,
         parsed_fields,
diff --git a/pypinfo/core.py b/pypinfo/core.py
index 87f0ffc..07c2ef5 100644
--- a/pypinfo/core.py
+++ b/pypinfo/core.py
@@ -1,7 +1,8 @@
+import calendar
 import json
 import os
 import re
-from datetime import datetime
+from datetime import date, datetime
 
 from google.cloud.bigquery import Client
 from google.cloud.bigquery.job import QueryJobConfig
@@ -35,6 +36,23 @@ def normalize(name):
     return re.sub(r'[-_.]+', '-', name).lower()
 
 
+def normalize_dates(start_date, end_date):
+    """If a date is yyyy-mm, normalize as first or last yyyy-mm-dd of the month.
+    Otherwise, return unchanged.
+    """
+    try:
+        start_date, _ = month_ends(start_date)  # yyyy-mm
+    except (AttributeError, ValueError):
+        pass  # -n, yyyy-mm-dd
+
+    try:
+        _, end_date = month_ends(end_date)  # yyyy-mm
+    except (AttributeError, ValueError):
+        pass  # -n, yyyy-mm-dd
+
+    return start_date, end_date
+
+
 def create_client(creds_file=None):
     creds_file = creds_file or os.environ.get('GOOGLE_APPLICATION_CREDENTIALS')
 
@@ -59,7 +77,7 @@ def validate_date(date_text):
     except ValueError:
         pass
 
-    raise ValueError('Dates must be negative integers or YYYY-MM-DD in the past.')
+    raise ValueError('Dates must be negative integers or YYYY-MM[-DD] in the past.')
 
 
 def format_date(date, timestamp_format):
@@ -70,6 +88,15 @@ def format_date(date, timestamp_format):
     return date
 
 
+def month_ends(yyyy_mm):
+    """Helper to return start_date and end_date of a month as yyyy-mm-dd"""
+    year, month = map(int, yyyy_mm.split("-"))
+    first = date(year, month, 1)
+    number_of_days = calendar.monthrange(year, month)[1]
+    last = date(year, month, number_of_days)
+    return str(first), str(last)
+
+
 def build_query(
     project, all_fields, start_date=None, end_date=None, days=None, limit=None, where=None, order=None, pip=None
 ):
@@ -82,6 +109,7 @@ def build_query(
     if days:
         start_date = str(int(end_date) - int(days))
 
+    start_date, end_date = normalize_dates(start_date, end_date)
     validate_date(start_date)
     validate_date(end_date)
 

new option start-month (or: allow incomplete date YYYY-MM)
This is possibly related to #47 as well as #48.

I would like to have statistics for each month, and this is achievable throught `--start-date` and `--end-date` options, but for convenience it would be nice to be able to just specify the month like 2018-10. 

This could be either done using a new option `--start-month` or by allowing a start-date without the DD, that is automatically extended to first day of the month when used for start-date and to last day of the month when used as end-date.
For both ways, start-month or incomplete start-date, the end-month or end-date should be automatically set to the (last day of the) same month by default, but changeable by the user.

Does this make sense? 
diff --git a/tests/test_core.py b/tests/test_core.py
index 9384b85..b61db87 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -118,6 +118,53 @@ def test_format_date_yyy_mm_dd():
     assert date == 'TIMESTAMP("2018-05-15 00:00:00")'
 
 
+def test_month_yyyy_mm():
+    # Act
+    first, last = core.month_ends("2019-03")
+
+    # Assert
+    assert first == "2019-03-01"
+    assert last == "2019-03-31"
+
+
+def test_month_yyyy_mm_dd():
+    # Act / Assert
+    with pytest.raises(ValueError):
+        core.month_ends("2019-03-18")
+
+
+def test_month_negative_integer():
+    # Act / Assert
+    with pytest.raises(AttributeError):
+        core.month_ends(-30)
+
+
+def test_normalize_dates_yyy_mm():
+    # Arrange
+    start_date = "2019-03"
+    end_date = "2019-03"
+
+    # Act
+    start_date, end_date = core.normalize_dates(start_date, end_date)
+
+    # Assert
+    assert start_date == "2019-03-01"
+    assert end_date == "2019-03-31"
+
+
+def test_normalize_dates_yyy_mm_dd_and_negative_integer():
+    # Arrange
+    start_date = "2019-03-18"
+    end_date = -1
+
+    # Act
+    start_date, end_date = core.normalize_dates(start_date, end_date)
+
+    # Assert
+    assert start_date == "2019-03-18"
+    assert end_date == -1
+
+
 def test_add_percentages():
     # Arrange
     rows = [

pip install -e .[dev]
parse_log_pytest
pytest
pytest
3.9
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
appdirs==1.4.4
binary==1.0.1
cachetools==5.5.2
certifi==2025.1.31
charset-normalizer==3.4.1
click==8.1.8
exceptiongroup @ file:///croot/exceptiongroup_1706031385326/work
google-api-core==2.24.2
google-auth==2.38.0
google-cloud-bigquery==3.31.0
google-cloud-core==2.4.3
google-crc32c==1.7.1
google-resumable-media==2.7.2
googleapis-common-protos==1.69.2
grpcio==1.71.0
grpcio-status==1.71.0
idna==3.10
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
packaging @ file:///croot/packaging_1734472117206/work
pluggy @ file:///croot/pluggy_1733169602837/work
proto-plus==1.26.1
protobuf==5.29.4
pyasn1==0.6.1
pyasn1_modules==0.4.2
-e git+https://github.com/ofek/pypinfo.git@48d56e690d7667ae5854752c3a2dc07e321d5637#egg=pypinfo
pytest @ file:///croot/pytest_1738938843180/work
python-dateutil==2.9.0.post0
requests==2.32.3
rsa==4.9
six==1.17.0
tinydb==4.8.2
tinyrecord==0.2.0
tomli @ file:///opt/conda/conda-bld/tomli_1657175507142/work
urllib3==2.3.0

name: pypinfo
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:
      - appdirs==1.4.4
      - binary==1.0.1
      - cachetools==5.5.2
      - certifi==2025.1.31
      - charset-normalizer==3.4.1
      - click==8.1.8
      - google-api-core==2.24.2
      - google-auth==2.38.0
      - google-cloud-bigquery==3.31.0
      - google-cloud-core==2.4.3
      - google-crc32c==1.7.1
      - google-resumable-media==2.7.2
      - googleapis-common-protos==1.69.2
      - grpcio==1.71.0
      - grpcio-status==1.71.0
      - idna==3.10
      - proto-plus==1.26.1
      - protobuf==5.29.4
      - pyasn1==0.6.1
      - pyasn1-modules==0.4.2
      - pypinfo==15.0.0
      - python-dateutil==2.9.0.post0
      - requests==2.32.3
      - rsa==4.9
      - six==1.17.0
      - tinydb==4.8.2
      - tinyrecord==0.2.0
      - urllib3==2.3.0
prefix: /opt/conda/envs/pypinfo

tests/test_core.py::test_month_yyyy_mm tests/test_core.py::test_month_yyyy_mm_dd tests/test_core.py::test_normalize_dates_yyy_mm tests/test_core.py::test_normalize_dates_yyy_mm_dd_and_negative_integer
tests/test_core.py::test_tabulate_default tests/test_core.py::test_tabulate_markdown tests/test_core.py::test_validate_date_negative_number tests/test_core.py::test_validate_date_positive_number tests/test_core.py::test_validate_date_valid_yyyy_mm_dd tests/test_core.py::test_validate_date_invalid_yyyy_mm_dd tests/test_core.py::test_validate_date_other_string tests/test_core.py::test_format_date_negative_number tests/test_core.py::test_format_date_yyy_mm_dd tests/test_core.py::test_month_negative_integer tests/test_core.py::test_add_percentages tests/test_core.py::test_add_total
README.rst pypinfo/cli.py pypinfo/core.py
README.rst pypinfo/cli.py pypinfo/core.py