diff --git a/Makefile b/Makefile
deleted file mode 100644
index 296e6d4..0000000
--- a/Makefile
+++ /dev/null
@@ -1,87 +0,0 @@
-.PHONY: clean clean-test clean-pyc clean-build docs help
-.DEFAULT_GOAL := help
-define BROWSER_PYSCRIPT
-import os, webbrowser, sys
-try:
-	from urllib import pathname2url
-except:
-	from urllib.request import pathname2url
-
-webbrowser.open("file://" + pathname2url(os.path.abspath(sys.argv[1])))
-endef
-export BROWSER_PYSCRIPT
-
-define PRINT_HELP_PYSCRIPT
-import re, sys
-
-for line in sys.stdin:
-	match = re.match(r'^([a-zA-Z_-]+):.*?## (.*)$$', line)
-	if match:
-		target, help = match.groups()
-		print("%-20s %s" % (target, help))
-endef
-export PRINT_HELP_PYSCRIPT
-BROWSER := python -c "$$BROWSER_PYSCRIPT"
-
-help:
-	@python -c "$$PRINT_HELP_PYSCRIPT" < $(MAKEFILE_LIST)
-
-clean: clean-build clean-pyc clean-test ## remove all build, test, coverage and Python artifacts
-
-
-clean-build: ## remove build artifacts
-	rm -fr build/
-	rm -fr dist/
-	rm -fr .eggs/
-	find . -name '*.egg-info' -exec rm -fr {} +
-	find . -name '*.egg' -exec rm -f {} +
-
-clean-pyc: ## remove Python file artifacts
-	find . -name '*.pyc' -exec rm -f {} +
-	find . -name '*.pyo' -exec rm -f {} +
-	find . -name '*~' -exec rm -f {} +
-	find . -name '__pycache__' -exec rm -fr {} +
-
-clean-test: ## remove test and coverage artifacts
-	rm -fr .tox/
-	rm -f .coverage
-	rm -fr htmlcov/
-
-lint: ## check style with flake8
-	flake8 ec2_reaper tests
-
-test: ## run tests quickly with the default Python
-	py.test
-	
-
-test-all: ## run tests on every Python version with tox
-	tox
-
-coverage: ## check code coverage quickly with the default Python
-	coverage run --source ec2_reaper -m pytest
-	coverage report -m
-	coverage html
-	$(BROWSER) htmlcov/index.html
-
-docs: ## generate Sphinx HTML documentation, including API docs
-	rm -f docs/ec2_reaper.rst
-	rm -f docs/modules.rst
-	sphinx-apidoc -o docs/ ec2_reaper
-	$(MAKE) -C docs clean
-	$(MAKE) -C docs html
-	$(BROWSER) docs/_build/html/index.html
-
-servedocs: docs ## compile the docs watching for changes
-	watchmedo shell-command -p '*.rst' -c '$(MAKE) -C docs html' -R -D .
-
-release: clean ## package and upload a release
-	python setup.py sdist upload
-	python setup.py bdist_wheel upload
-
-dist: clean ## builds source and wheel package
-	python setup.py sdist
-	python setup.py bdist_wheel
-	ls -l dist
-
-install: clean ## install the package to the active Python's site-packages
-	python setup.py install
diff --git a/ec2_reaper/aws_lambda.py b/ec2_reaper/aws_lambda.py
index ea519d0..c51e330 100644
--- a/ec2_reaper/aws_lambda.py
+++ b/ec2_reaper/aws_lambda.py
@@ -14,8 +14,6 @@ def _is_py3():
 
 log = logging.getLogger(__name__)
 
-DEFAULT_SLACK_ENDPOINT = ''
-
 MIN_AGE = os.environ.get('MIN_AGE', ec2_reaper.DEFAULT_MIN_AGE)
 REGIONS = os.environ.get('REGIONS', ec2_reaper.DEFAULT_REGIONS)
 REGIONS = REGIONS.split(' ') if isinstance(REGIONS, str) else REGIONS
@@ -36,7 +34,7 @@ else:
     logging.getLogger('botocore').setLevel(logging.WARNING)
     logging.getLogger('boto3').setLevel(logging.WARNING)
 
-# this is necessary because tz-aware dts aren't JSON serializable by default
+# so that we can send tz-aware datetimes through json
 class DateTimeJSONEncoder(json.JSONEncoder):
     def default(self, o):
         if isinstance(o, datetime):
@@ -47,7 +45,7 @@ def _respond(body, error=True, headers=None, status_code=500):
     o = {'statusCode': status_code, 'body': body}
     if headers:
         o['headers'] = headers
-    return json.dumps(o, cls=DateTimeJSONEncoder)
+    return o
 
 def _get_expires(launch_time, min_age=MIN_AGE):
     # if launch_time is naive, assume UTC
@@ -66,7 +64,8 @@ def _notify(msg, attachments=[]):
 
     data = {'text': msg, 'attachements': attachments}
     headers = {'Content-Type': 'application/json'}
-    r = requests.post(SLACK_ENDPOINT, json=data, headers=headers)
+    r = requests.post(SLACK_ENDPOINT, headers=headers,
+                      data=json.dumps(data, cls=DateTimeJSONEncoder))
 
     if r.status_code != 200:
         log.error('Slack notification failed: (HTTP {}) {}'.format(r.status_code, r.text))
@@ -127,4 +126,7 @@ def handler(event, context):
             })
         _notify(msg, attachments)
 
-    return _respond(reaperlog, error=False, status_code=200)
+    r = {'reaped': len(reaped), 'matches_under_min_age': len(too_young),
+         'tag_matches': len([i for i in reaperlog if i['tag_match']]),
+         'instances': len(reaperlog), 'log': reaperlog}
+    return _respond(r, error=False, status_code=200)

Unserializable datetimes strike again!
```
$ sls invoke -f cron
{
    "stackTrace": [
        [
            "/var/task/handler.py",
            4,
            "run",
            "return ec2_reaper.aws_lambda.handler(event, context)"
        ],
        [
            "/var/task/ec2_reaper/aws_lambda.py",
            128,
            "handler",
            "_notify(msg, attachments)"
        ],
        [
            "/var/task/ec2_reaper/aws_lambda.py",
            69,
            "_notify",
            "r = requests.post(SLACK_ENDPOINT, json=data, headers=headers)"
        ],
        [
            "/var/task/requests/api.py",
            112,
            "post",
            "return request('post', url, data=data, json=json, **kwargs)"
        ],
        [
            "/var/task/requests/api.py",
            58,
            "request",
            "return session.request(method=method, url=url, **kwargs)"
        ],
        [
            "/var/task/requests/sessions.py",
            494,
            "request",
            "prep = self.prepare_request(req)"
        ],
        [
            "/var/task/requests/sessions.py",
            437,
            "prepare_request",
            "hooks=merge_hooks(request.hooks, self.hooks),"
        ],
        [
            "/var/task/requests/models.py",
            308,
            "prepare",
            "self.prepare_body(data, files, json)"
        ],
        [
            "/var/task/requests/models.py",
            458,
            "prepare_body",
            "body = complexjson.dumps(json)"
        ],
        [
            "/usr/lib64/python2.7/json/__init__.py",
            244,
            "dumps",
            "return _default_encoder.encode(obj)"
        ],
        [
            "/usr/lib64/python2.7/json/encoder.py",
            207,
            "encode",
            "chunks = self.iterencode(o, _one_shot=True)"
        ],
        [
            "/usr/lib64/python2.7/json/encoder.py",
            270,
            "iterencode",
            "return _iterencode(o, 0)"
        ],
        [
            "/usr/lib64/python2.7/json/encoder.py",
            184,
            "default",
            "raise TypeError(repr(o) + \" is not JSON serializable\")"
        ]
    ],
    "errorType": "TypeError",
    "errorMessage": "datetime.datetime(2017, 12, 15, 20, 4, 33, tzinfo=tzlocal()) is not JSON serializable"
}
 
  Error --------------------------------------------------
 
  Invoked function failed
 
     For debugging logs, run again after setting the "SLS_DEBUG=*" environment variable.
 
  Get Support --------------------------------------------
     Docs:          docs.serverless.com
     Bugs:          github.com/serverless/serverless/issues
     Forums:        forum.serverless.com
     Chat:          gitter.im/serverless/serverless
 
  Your Environment Information -----------------------------
     OS:                     linux
     Node Version:           6.11.4
     Serverless Version:     1.24.1
 
```
diff --git a/tests/test_lambda_handler.py b/tests/test_lambda_handler.py
index 08bd129..bc153b4 100644
--- a/tests/test_lambda_handler.py
+++ b/tests/test_lambda_handler.py
@@ -10,10 +10,11 @@ logging.basicConfig(level=logging.DEBUG)
 logging.getLogger('botocore').setLevel(logging.INFO)
 logging.getLogger('boto3').setLevel(logging.INFO)
 
-if sys.version_info >= (3, 0):
-    from unittest.mock import patch
-else:
+if sys.version_info < (3, 0) or (sys.version_info >= (3, 5) and
+                                 sys.version_info < (3, 6)):
     from mock import patch
+else:
+    from unittest.mock import patch
 
 # when no results, handler should have called reap, *not* called (slack) notify,
 # and should have returned a happy response json obj,
@@ -21,12 +22,13 @@ else:
 @patch.object(aws_lambda, '_notify')
 def test_reap_no_results(mock_notify, mock_reap):
     mock_reap.return_value = []
-    r = json.loads(aws_lambda.handler({}, {}))
+    r = aws_lambda.handler({}, {})
 
     mock_notify.assert_not_called()
     mock_reap.assert_called_once()
     assert r['statusCode'] == 200
-    assert r['body'] == []
+    assert r['body']['log'] == []
+    assert r['body']['reaped'] == 0
 
 # with pos and neg results, handler should have called reap,
 # called (slack) notify, and should have returned a happy response json obj with
@@ -46,9 +48,10 @@ def test_reap_2neg_1pos(mock_notify, mock_reap):
          'launch_time': match_time, 'reaped': True, 'region': 'us-east-1'},
     ]
     mock_reap.return_value = mock_reap_results
-    r = json.loads(aws_lambda.handler({}, {}))
+    r = aws_lambda.handler({}, {})
 
     mock_notify.assert_called()
     mock_reap.assert_called_once()
     assert r['statusCode'] == 200
-    assert r['body'] == json.loads(json.dumps(mock_reap_results, cls=aws_lambda.DateTimeJSONEncoder))
+    assert r['body']['log'] == mock_reap_results
+    assert r['body']['reaped'] == 1

pip install -e .[dev]
parse_log_pytest
requirements.txt
pytest
apt-get update apt-get install -y gcc
3.6
requirements.txt requirements_dev.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
alabaster==0.7.13
attrs==22.2.0
Babel==2.11.0
boto3==1.23.10
botocore==1.26.10
bump2version==1.0.1
bumpversion==0.6.0
certifi==2021.5.30
cffi==1.15.1
charset-normalizer==2.0.12
click==8.0.4
coverage==6.2
cryptography==40.0.2
dataclasses==0.8
distlib==0.3.9
docutils==0.17.1
-e git+https://github.com/inhumantsar/python-ec2-reaper.git@9e69a1f1bf4e98c8d22ee62b5870bbb0a39b91be#egg=ec2_reaper
filelock==3.4.1
flake8==5.0.4
idna==3.10
imagesize==1.4.1
importlib-metadata==4.2.0
importlib-resources==5.4.0
iniconfig==1.1.1
Jinja2==3.0.3
jmespath==0.10.0
MarkupSafe==2.0.1
mccabe==0.7.0
mock==5.2.0
moto==4.0.13
packaging==21.3
platformdirs==2.4.0
pluggy==1.0.0
py==1.11.0
pycodestyle==2.9.1
pycparser==2.21
pyflakes==2.5.0
Pygments==2.14.0
pyparsing==3.1.4
pytest==7.0.1
pytest-runner==5.3.2
python-dateutil==2.9.0.post0
pytz==2025.2
PyYAML==6.0.1
requests==2.27.1
responses==0.17.0
s3transfer==0.5.2
six==1.17.0
slacker==0.14.0
snowballstemmer==2.2.0
Sphinx==4.3.2
sphinxcontrib-applehelp==1.0.2
sphinxcontrib-devhelp==1.0.2
sphinxcontrib-htmlhelp==2.0.0
sphinxcontrib-jsmath==1.0.1
sphinxcontrib-qthelp==1.0.3
sphinxcontrib-serializinghtml==1.1.5
toml==0.10.2
tomli==1.2.3
tox==3.28.0
typing_extensions==4.1.1
urllib3==1.26.20
virtualenv==20.16.2
watchdog==2.3.1
Werkzeug==2.0.3
xmltodict==0.14.2
zipp==3.6.0

name: python-ec2-reaper
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
  - certifi=2021.5.30=py36h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - 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=1.1.1w=h7f8727e_0
  - pip=21.2.2=py36h06a4308_0
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - alabaster==0.7.13
      - attrs==22.2.0
      - babel==2.11.0
      - boto3==1.23.10
      - botocore==1.26.10
      - bump2version==1.0.1
      - bumpversion==0.6.0
      - cffi==1.15.1
      - charset-normalizer==2.0.12
      - click==8.0.4
      - coverage==6.2
      - cryptography==40.0.2
      - dataclasses==0.8
      - distlib==0.3.9
      - docutils==0.17.1
      - filelock==3.4.1
      - flake8==5.0.4
      - idna==3.10
      - imagesize==1.4.1
      - importlib-metadata==4.2.0
      - importlib-resources==5.4.0
      - iniconfig==1.1.1
      - jinja2==3.0.3
      - jmespath==0.10.0
      - markupsafe==2.0.1
      - mccabe==0.7.0
      - mock==5.2.0
      - moto==4.0.13
      - packaging==21.3
      - platformdirs==2.4.0
      - pluggy==1.0.0
      - py==1.11.0
      - pycodestyle==2.9.1
      - pycparser==2.21
      - pyflakes==2.5.0
      - pygments==2.14.0
      - pyparsing==3.1.4
      - pytest==7.0.1
      - pytest-runner==5.3.2
      - python-dateutil==2.9.0.post0
      - pytz==2025.2
      - pyyaml==6.0.1
      - requests==2.27.1
      - responses==0.17.0
      - s3transfer==0.5.2
      - six==1.17.0
      - slacker==0.14.0
      - snowballstemmer==2.2.0
      - sphinx==4.3.2
      - sphinxcontrib-applehelp==1.0.2
      - sphinxcontrib-devhelp==1.0.2
      - sphinxcontrib-htmlhelp==2.0.0
      - sphinxcontrib-jsmath==1.0.1
      - sphinxcontrib-qthelp==1.0.3
      - sphinxcontrib-serializinghtml==1.1.5
      - toml==0.10.2
      - tomli==1.2.3
      - tox==3.28.0
      - typing-extensions==4.1.1
      - urllib3==1.26.20
      - virtualenv==20.16.2
      - watchdog==2.3.1
      - werkzeug==2.0.3
      - xmltodict==0.14.2
      - zipp==3.6.0
prefix: /opt/conda/envs/python-ec2-reaper

tests/test_lambda_handler.py::test_reap_no_results tests/test_lambda_handler.py::test_reap_2neg_1pos
Makefile ec2_reaper/aws_lambda.py
Makefile ec2_reaper/aws_lambda.py