Repository: Stranger6667/postmarker

Issue:
Stats API
http://developer.postmarkapp.com/developer-api-stats.html

Discussion:
codecov-io: # [Codecov](https://codecov.io/gh/Stranger6667/postmarker/pull/81?src=pr&el=h1) Report
> Merging [#81](https://codecov.io/gh/Stranger6667/postmarker/pull/81?src=pr&el=desc) into [master](https://codecov.io/gh/Stranger6667/postmarker/commit/f4e41e0cd842d9c5b638428fb7e9271211bb0db2?src=pr&el=desc) will **not impact** coverage.


```diff
@@          Coverage Diff          @@
##           master    #81   +/-   ##
=====================================
  Coverage     100%   100%           
=====================================
  Files          14     15    +1     
  Lines         507    537   +30     
  Branches       51     51           
=====================================
+ Hits          507    537   +30
```


| [Impacted Files](https://codecov.io/gh/Stranger6667/postmarker/pull/81?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [postmarker/models/stats.py](https://codecov.io/gh/Stranger6667/postmarker/compare/f4e41e0cd842d9c5b638428fb7e9271211bb0db2...9f8a766234842bcced52e636aca2d993e7bf6f38?src=pr&el=tree#diff-cG9zdG1hcmtlci9tb2RlbHMvc3RhdHMucHk=) | `100% <100%> (ø)` | |
| [postmarker/__init__.py](https://codecov.io/gh/Stranger6667/postmarker/compare/f4e41e0cd842d9c5b638428fb7e9271211bb0db2...9f8a766234842bcced52e636aca2d993e7bf6f38?src=pr&el=tree#diff-cG9zdG1hcmtlci9fX2luaXRfXy5weQ==) | `100% <100%> (ø)` | :white_check_mark: |
| [postmarker/core.py](https://codecov.io/gh/Stranger6667/postmarker/compare/f4e41e0cd842d9c5b638428fb7e9271211bb0db2...9f8a766234842bcced52e636aca2d993e7bf6f38?src=pr&el=tree#diff-cG9zdG1hcmtlci9jb3JlLnB5) | `100% <100%> (ø)` | :white_check_mark: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/Stranger6667/postmarker/pull/81?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/Stranger6667/postmarker/pull/81?src=pr&el=footer). Last update [f4e41e0...9f8a766](https://codecov.io/gh/Stranger6667/postmarker/compare/f4e41e0cd842d9c5b638428fb7e9271211bb0db2...9f8a766234842bcced52e636aca2d993e7bf6f38?src=pr&el=footer&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

Patch:
diff --git a/README.rst b/README.rst
index 657bbba..f434803 100644
--- a/README.rst
+++ b/README.rst
@@ -77,7 +77,7 @@ Setup an email:
 There are a lot of features available. Check it out in our documentation! Here's just a few of them:
 
 - Support for sending Python email instances.
-- Bounces, Domains, Templates, Status & Server API.
+- Bounces, Domains, Templates, Status, Stats & Server API.
 - Django email backend.
 - Spam check API.
 
diff --git a/docs/changelog.rst b/docs/changelog.rst
index aaebab2..808978a 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -3,6 +3,11 @@
 Changelog
 =========
 
+0.7.0.dev0 - TBA
+----------------
+
+- Stats API. (`#72`_)
+
 0.6.2 - 02.01.2017
 ------------------
 
@@ -91,6 +96,7 @@ Changelog
 
 .. _#78: https://github.com/Stranger6667/postmarker/issues/78
 .. _#76: https://github.com/Stranger6667/postmarker/issues/76
+.. _#72: https://github.com/Stranger6667/postmarker/issues/72
 .. _#64: https://github.com/Stranger6667/postmarker/issues/64
 .. _#62: https://github.com/Stranger6667/postmarker/issues/62
 .. _#60: https://github.com/Stranger6667/postmarker/issues/60
diff --git a/docs/index.rst b/docs/index.rst
index cc46d34..9ca6d91 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -20,6 +20,7 @@ Contents:
    bounces
    server
    status
+   stats
    domains
    django
    spamcheck
diff --git a/docs/stats.rst b/docs/stats.rst
new file mode 100644
index 0000000..e57fba7
--- /dev/null
+++ b/docs/stats.rst
@@ -0,0 +1,36 @@
+.. _stats:
+
+Stats
+=====
+
+The Stats API is available via the ``stats`` manager:
+
+.. code-block:: python
+
+    >>> postmark.stats.overview(fromdate='2017-02-01')
+    {
+        'BounceRate': 0.0,
+        'Bounced': 0,
+        'Opens': 50,
+        'SMTPApiErrors': 0,
+        'Sent': 93,
+        'SpamComplaints': 0,
+        'SpamComplaintsRate': 0.0,
+        'TotalClicks': 0,
+        'TotalTrackedLinksSent': 0,
+        'Tracked': 93,
+        'UniqueLinksClicked': 0,
+        'UniqueOpens': 19,
+        'WithClientRecorded': 13,
+        'WithLinkTracking': 0,
+        'WithOpenTracking': 93,
+        'WithPlatformRecorded': 19,
+        'WithReadTimeRecorded': 19
+    }
+    >>> postmark.stats.sends(fromdate='2017-02-01')
+    {
+        'Days': [
+            {'Date': '2017-02-01', 'Sent': 2}
+        ],
+        'Sent': 2
+    }
diff --git a/postmarker/__init__.py b/postmarker/__init__.py
index 041b770..e2d156c 100644
--- a/postmarker/__init__.py
+++ b/postmarker/__init__.py
@@ -1,4 +1,4 @@
 # coding: utf-8
 
 
-__version__ = '0.6.2'
+__version__ = '0.7.0.dev0'
diff --git a/postmarker/core.py b/postmarker/core.py
index d1766fb..9d25763 100644
--- a/postmarker/core.py
+++ b/postmarker/core.py
@@ -9,6 +9,7 @@ from .models.bounces import BounceManager
 from .models.domains import DomainsManager
 from .models.emails import EmailManager
 from .models.server import ServerManager
+from .models.stats import StatsManager
 from .models.status import StatusManager
 from .models.templates import TemplateManager
 
@@ -51,6 +52,7 @@ class PostmarkClient(with_metaclass(ClientMeta)):
         DomainsManager,
         EmailManager,
         ServerManager,
+        StatsManager,
         StatusManager,
         TemplateManager,
     )
diff --git a/postmarker/models/stats.py b/postmarker/models/stats.py
new file mode 100644
index 0000000..1691aa8
--- /dev/null
+++ b/postmarker/models/stats.py
@@ -0,0 +1,92 @@
+# coding: utf-8
+from .base import ModelManager
+
+
+class StatsManager(ModelManager):
+    name = 'stats'
+
+    def overview(self, tag=None, fromdate=None, todate=None):
+        """
+        Gets a brief overview of statistics for all of your outbound email.
+        """
+        return self.call('GET', '/stats/outbound', tag=tag, fromdate=fromdate, todate=todate)
+
+    def sends(self, tag=None, fromdate=None, todate=None):
+        """
+        Gets a total count of emails you’ve sent out.
+        """
+        return self.call('GET', '/stats/outbound/sends', tag=tag, fromdate=fromdate, todate=todate)
+
+    def bounces(self, tag=None, fromdate=None, todate=None):
+        """
+        Gets total counts of emails you’ve sent out that have been returned as bounced.
+        """
+        return self.call('GET', '/stats/outbound/bounces', tag=tag, fromdate=fromdate, todate=todate)
+
+    def spam(self, tag=None, fromdate=None, todate=None):
+        """
+        Gets a total count of recipients who have marked your email as spam.
+        """
+        return self.call('GET', '/stats/outbound/spam', tag=tag, fromdate=fromdate, todate=todate)
+
+    def tracked(self, tag=None, fromdate=None, todate=None):
+        """
+        Gets a total count of emails you’ve sent with open tracking or link tracking enabled.
+        """
+        return self.call('GET', '/stats/outbound/tracked', tag=tag, fromdate=fromdate, todate=todate)
+
+    def opens(self, tag=None, fromdate=None, todate=None):
+        """
+        Gets total counts of recipients who opened your emails.
+        This is only recorded when open tracking is enabled for that email.
+        """
+        return self.call('GET', '/stats/outbound/opens', tag=tag, fromdate=fromdate, todate=todate)
+
+    def opens_platforms(self, tag=None, fromdate=None, todate=None):
+        """
+        Gets an overview of the platforms used to open your emails.
+        This is only recorded when open tracking is enabled for that email.
+        """
+        return self.call('GET', '/stats/outbound/opens/platforms', tag=tag, fromdate=fromdate, todate=todate)
+
+    def emailclients(self, tag=None, fromdate=None, todate=None):
+        """
+        Gets an overview of the email clients used to open your emails.
+        This is only recorded when open tracking is enabled for that email.
+        """
+        return self.call('GET', '/stats/outbound/opens/emailclients', tag=tag, fromdate=fromdate, todate=todate)
+
+    def readtimes(self, tag=None, fromdate=None, todate=None):
+        """
+        Gets the length of time that recipients read emails along with counts for each time.
+        This is only recorded when open tracking is enabled for that email.
+        Read time tracking stops at 20 seconds, so any read times above that will appear in the 20s+ field.
+        """
+        return self.call('GET', '/stats/outbound/opens/readtimes', tag=tag, fromdate=fromdate, todate=todate)
+
+    def clicks(self, tag=None, fromdate=None, todate=None):
+        """
+        Gets total counts of unique links that were clicked.
+        """
+        return self.call('GET', '/stats/outbound/clicks', tag=tag, fromdate=fromdate, todate=todate)
+
+    def browserfamilies(self, tag=None, fromdate=None, todate=None):
+        """
+        Gets an overview of the browsers used to open links in your emails.
+        This is only recorded when Link Tracking is enabled for that email.
+        """
+        return self.call('GET', '/stats/outbound/clicks/browserfamilies', tag=tag, fromdate=fromdate, todate=todate)
+
+    def clicks_platforms(self, tag=None, fromdate=None, todate=None):
+        """
+        Gets an overview of the browser platforms used to open your emails.
+        This is only recorded when Link Tracking is enabled for that email.
+        """
+        return self.call('GET', '/stats/outbound/clicks/platforms', tag=tag, fromdate=fromdate, todate=todate)
+
+    def location(self, tag=None, fromdate=None, todate=None):
+        """
+        Gets an overview of which part of the email links were clicked from (HTML or Text).
+        This is only recorded when Link Tracking is enabled for that email.
+        """
+        return self.call('GET', '/stats/outbound/clicks/location', tag=tag, fromdate=fromdate, todate=todate)


Test patch:
diff --git a/tests/cassettes/stats.json b/tests/cassettes/stats.json
new file mode 100644
index 0000000..27e2e73
--- /dev/null
+++ b/tests/cassettes/stats.json
@@ -0,0 +1,799 @@
+{
+  "http_interactions": [
+    {
+      "recorded_at": "2017-02-01T08:29:23",
+      "request": {
+        "body": {
+          "base64_string": "",
+          "encoding": "utf-8"
+        },
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Accept-Encoding": [
+            "gzip, deflate"
+          ],
+          "Connection": [
+            "keep-alive"
+          ],
+          "User-Agent": [
+            "Postmarker/0.7.0.dev0"
+          ],
+          "X-Postmark-Server-Token": [
+            "SERVER_TOKEN"
+          ]
+        },
+        "method": "GET",
+        "uri": "https://api.postmarkapp.com/stats/outbound?fromdate=2017-01-30"
+      },
+      "response": {
+        "body": {
+          "base64_string": "H4sIAAAAAAAEAGWQyw6CMBBF/6VrYiDEBeyUuNNAAOO6KaNO6MtSVsZ/tw+iGJdz7s3MaZ+kA2lJWeQJ2atZMhhImSakO/XNTuPBGGWmQGLaUgtu3PiKpqJSQnOK0sbOL1p1e0PZ6Ff7O7UG6fpbx88SHzMsICtcUVnKK45sjBtj4YhynAJd9C5o72526i0wZQaPszzyhlN7VUaskiImLdChRwH/iT8QHFHePge81hd682C3vCU4xc9LX29XpLGhRwEAAA==",
+          "encoding": "utf-8"
+        },
+        "headers": {
+          "Cache-Control": [
+            "private"
+          ],
+          "Content-Encoding": [
+            "gzip"
+          ],
+          "Content-Length": [
+            "196"
+          ],
+          "Content-Type": [
+            "application/json; charset=utf-8"
+          ],
+          "Date": [
+            "Wed, 01 Feb 2017 08:29:22 GMT"
+          ],
+          "Server": [
+            "unicorns-double-rainbow"
+          ],
+          "X-Powered-By": [
+            "ASP.NET"
+          ]
+        },
+        "status": {
+          "code": 200,
+          "message": "OK"
+        },
+        "url": "https://api.postmarkapp.com/stats/outbound?fromdate=2017-01-30"
+      }
+    },
+    {
+      "recorded_at": "2017-02-01T08:29:23",
+      "request": {
+        "body": {
+          "base64_string": "",
+          "encoding": "utf-8"
+        },
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Accept-Encoding": [
+            "gzip, deflate"
+          ],
+          "Connection": [
+            "keep-alive"
+          ],
+          "User-Agent": [
+            "Postmarker/0.7.0.dev0"
+          ],
+          "X-Postmark-Server-Token": [
+            "SERVER_TOKEN"
+          ]
+        },
+        "method": "GET",
+        "uri": "https://api.postmarkapp.com/stats/outbound/sends?fromdate=2017-01-30"
+      },
+      "response": {
+        "body": {
+          "base64_string": "H4sIAAAAAAAEAKtWckmsLFayiq4GMkpSlayUjAwMzXUNDHWNDZR0lIJT80qUrIzNa3Uw5Q3h8qYmGPJGQCVweaPaWBjT0rgWAJGKnXdzAAAA",
+          "encoding": "utf-8"
+        },
+        "headers": {
+          "Cache-Control": [
+            "private"
+          ],
+          "Content-Encoding": [
+            "gzip"
+          ],
+          "Content-Length": [
+            "81"
+          ],
+          "Content-Type": [
+            "application/json; charset=utf-8"
+          ],
+          "Date": [
+            "Wed, 01 Feb 2017 08:29:23 GMT"
+          ],
+          "Server": [
+            "unicorns-double-rainbow"
+          ],
+          "X-Powered-By": [
+            "ASP.NET"
+          ]
+        },
+        "status": {
+          "code": 200,
+          "message": "OK"
+        },
+        "url": "https://api.postmarkapp.com/stats/outbound/sends?fromdate=2017-01-30"
+      }
+    },
+    {
+      "recorded_at": "2017-02-01T08:29:23",
+      "request": {
+        "body": {
+          "base64_string": "",
+          "encoding": "utf-8"
+        },
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Accept-Encoding": [
+            "gzip, deflate"
+          ],
+          "Connection": [
+            "keep-alive"
+          ],
+          "User-Agent": [
+            "Postmarker/0.7.0.dev0"
+          ],
+          "X-Postmark-Server-Token": [
+            "SERVER_TOKEN"
+          ]
+        },
+        "method": "GET",
+        "uri": "https://api.postmarkapp.com/stats/outbound/bounces?fromdate=2017-01-30"
+      },
+      "response": {
+        "body": {
+          "base64_string": "H4sIAAAAAAAEAKtWckmsLFayio6tBQBnJIrzCwAAAA==",
+          "encoding": "utf-8"
+        },
+        "headers": {
+          "Cache-Control": [
+            "private"
+          ],
+          "Content-Encoding": [
+            "gzip"
+          ],
+          "Content-Length": [
+            "31"
+          ],
+          "Content-Type": [
+            "application/json; charset=utf-8"
+          ],
+          "Date": [
+            "Wed, 01 Feb 2017 08:29:23 GMT"
+          ],
+          "Server": [
+            "unicorns-double-rainbow"
+          ],
+          "X-Powered-By": [
+            "ASP.NET"
+          ]
+        },
+        "status": {
+          "code": 200,
+          "message": "OK"
+        },
+        "url": "https://api.postmarkapp.com/stats/outbound/bounces?fromdate=2017-01-30"
+      }
+    },
+    {
+      "recorded_at": "2017-02-01T08:29:24",
+      "request": {
+        "body": {
+          "base64_string": "",
+          "encoding": "utf-8"
+        },
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Accept-Encoding": [
+            "gzip, deflate"
+          ],
+          "Connection": [
+            "keep-alive"
+          ],
+          "User-Agent": [
+            "Postmarker/0.7.0.dev0"
+          ],
+          "X-Postmark-Server-Token": [
+            "SERVER_TOKEN"
+          ]
+        },
+        "method": "GET",
+        "uri": "https://api.postmarkapp.com/stats/outbound/spam?fromdate=2017-01-30"
+      },
+      "response": {
+        "body": {
+          "base64_string": "H4sIAAAAAAAEAKtWckmsLFayio6tBQBnJIrzCwAAAA==",
+          "encoding": "utf-8"
+        },
+        "headers": {
+          "Cache-Control": [
+            "private"
+          ],
+          "Content-Encoding": [
+            "gzip"
+          ],
+          "Content-Length": [
+            "31"
+          ],
+          "Content-Type": [
+            "application/json; charset=utf-8"
+          ],
+          "Date": [
+            "Wed, 01 Feb 2017 08:29:23 GMT"
+          ],
+          "Server": [
+            "unicorns-double-rainbow"
+          ],
+          "X-Powered-By": [
+            "ASP.NET"
+          ]
+        },
+        "status": {
+          "code": 200,
+          "message": "OK"
+        },
+        "url": "https://api.postmarkapp.com/stats/outbound/spam?fromdate=2017-01-30"
+      }
+    },
+    {
+      "recorded_at": "2017-02-01T08:29:24",
+      "request": {
+        "body": {
+          "base64_string": "",
+          "encoding": "utf-8"
+        },
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Accept-Encoding": [
+            "gzip, deflate"
+          ],
+          "Connection": [
+            "keep-alive"
+          ],
+          "User-Agent": [
+            "Postmarker/0.7.0.dev0"
+          ],
+          "X-Postmark-Server-Token": [
+            "SERVER_TOKEN"
+          ]
+        },
+        "method": "GET",
+        "uri": "https://api.postmarkapp.com/stats/outbound/tracked?fromdate=2017-01-30"
+      },
+      "response": {
+        "body": {
+          "base64_string": "H4sIAAAAAAAEAKtWckmsLFayiq4GMkpSlayUjAwMzXUNDHWNDZR0lEKKEpOzU1OUrIzNa3UwlRgiKzE1wVBiBFSFrMSoNhaJZ2lcCwAfi9kTfwAAAA==",
+          "encoding": "utf-8"
+        },
+        "headers": {
+          "Cache-Control": [
+            "private"
+          ],
+          "Content-Encoding": [
+            "gzip"
+          ],
+          "Content-Length": [
+            "85"
+          ],
+          "Content-Type": [
+            "application/json; charset=utf-8"
+          ],
+          "Date": [
+            "Wed, 01 Feb 2017 08:29:23 GMT"
+          ],
+          "Server": [
+            "unicorns-double-rainbow"
+          ],
+          "X-Powered-By": [
+            "ASP.NET"
+          ]
+        },
+        "status": {
+          "code": 200,
+          "message": "OK"
+        },
+        "url": "https://api.postmarkapp.com/stats/outbound/tracked?fromdate=2017-01-30"
+      }
+    },
+    {
+      "recorded_at": "2017-02-01T08:29:24",
+      "request": {
+        "body": {
+          "base64_string": "",
+          "encoding": "utf-8"
+        },
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Accept-Encoding": [
+            "gzip, deflate"
+          ],
+          "Connection": [
+            "keep-alive"
+          ],
+          "User-Agent": [
+            "Postmarker/0.7.0.dev0"
+          ],
+          "X-Postmark-Server-Token": [
+            "SERVER_TOKEN"
+          ]
+        },
+        "method": "GET",
+        "uri": "https://api.postmarkapp.com/stats/outbound/opens?fromdate=2017-01-30"
+      },
+      "response": {
+        "body": {
+          "base64_string": "H4sIAAAAAAAEAKtWckmsLFayiq4GMkpSlayUjAwMzXUNDHWNDZR0lPwLUvOAsmY6SqF5mYWlQHnjWh1MpYYIpcaWCLWGmIqNgOoRik2RzY1FCBsgmWFZCwBUQPR8pAAAAA==",
+          "encoding": "utf-8"
+        },
+        "headers": {
+          "Cache-Control": [
+            "private"
+          ],
+          "Content-Encoding": [
+            "gzip"
+          ],
+          "Content-Length": [
+            "97"
+          ],
+          "Content-Type": [
+            "application/json; charset=utf-8"
+          ],
+          "Date": [
+            "Wed, 01 Feb 2017 08:29:23 GMT"
+          ],
+          "Server": [
+            "unicorns-double-rainbow"
+          ],
+          "X-Powered-By": [
+            "ASP.NET"
+          ]
+        },
+        "status": {
+          "code": 200,
+          "message": "OK"
+        },
+        "url": "https://api.postmarkapp.com/stats/outbound/opens?fromdate=2017-01-30"
+      }
+    },
+    {
+      "recorded_at": "2017-02-01T08:29:24",
+      "request": {
+        "body": {
+          "base64_string": "",
+          "encoding": "utf-8"
+        },
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Accept-Encoding": [
+            "gzip, deflate"
+          ],
+          "Connection": [
+            "keep-alive"
+          ],
+          "User-Agent": [
+            "Postmarker/0.7.0.dev0"
+          ],
+          "X-Postmark-Server-Token": [
+            "SERVER_TOKEN"
+          ]
+        },
+        "method": "GET",
+        "uri": "https://api.postmarkapp.com/stats/outbound/opens/platforms?fromdate=2017-01-30"
+      },
+      "response": {
+        "body": {
+          "base64_string": "H4sIAAAAAAAEAKtWckmsLFayiq4GMkpSlayUjAwMzXUNDHWNDZR0lFxSi7NL8guUrAx1lHzzkzJzUsHM0LzsvPzyPCC7VgdToyGyRiNk1ZjKjYA6lJCUGNfGImk2xmGraS0ApuqGybgAAAA=",
+          "encoding": "utf-8"
+        },
+        "headers": {
+          "Cache-Control": [
+            "private"
+          ],
+          "Content-Encoding": [
+            "gzip"
+          ],
+          "Content-Length": [
+            "107"
+          ],
+          "Content-Type": [
+            "application/json; charset=utf-8"
+          ],
+          "Date": [
+            "Wed, 01 Feb 2017 08:29:24 GMT"
+          ],
+          "Server": [
+            "unicorns-double-rainbow"
+          ],
+          "X-Powered-By": [
+            "ASP.NET"
+          ]
+        },
+        "status": {
+          "code": 200,
+          "message": "OK"
+        },
+        "url": "https://api.postmarkapp.com/stats/outbound/opens/platforms?fromdate=2017-01-30"
+      }
+    },
+    {
+      "recorded_at": "2017-02-01T08:29:24",
+      "request": {
+        "body": {
+          "base64_string": "",
+          "encoding": "utf-8"
+        },
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Accept-Encoding": [
+            "gzip, deflate"
+          ],
+          "Connection": [
+            "keep-alive"
+          ],
+          "User-Agent": [
+            "Postmarker/0.7.0.dev0"
+          ],
+          "X-Postmark-Server-Token": [
+            "SERVER_TOKEN"
+          ]
+        },
+        "method": "GET",
+        "uri": "https://api.postmarkapp.com/stats/outbound/opens/emailclients?fromdate=2017-01-30"
+      },
+      "response": {
+        "body": {
+          "base64_string": "H4sIAAAAAAAEAKtWckmsLFayiq4GMkpSlayUjAwMzXUNDHWNDZR0lNxzEzNzlKwMdZRCMkrzUlKLkjKLUoD8Wh1M9YYI9WZo6o0w1BsBtSDUG9fGIuwyQNNsXAsAPCp2d6UAAAA=",
+          "encoding": "utf-8"
+        },
+        "headers": {
+          "Cache-Control": [
+            "private"
+          ],
+          "Content-Encoding": [
+            "gzip"
+          ],
+          "Content-Length": [
+            "101"
+          ],
+          "Content-Type": [
+            "application/json; charset=utf-8"
+          ],
+          "Date": [
+            "Wed, 01 Feb 2017 08:29:24 GMT"
+          ],
+          "Server": [
+            "unicorns-double-rainbow"
+          ],
+          "X-Powered-By": [
+            "ASP.NET"
+          ]
+        },
+        "status": {
+          "code": 200,
+          "message": "OK"
+        },
+        "url": "https://api.postmarkapp.com/stats/outbound/opens/emailclients?fromdate=2017-01-30"
+      }
+    },
+    {
+      "recorded_at": "2017-02-01T08:29:24",
+      "request": {
+        "body": {
+          "base64_string": "",
+          "encoding": "utf-8"
+        },
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Accept-Encoding": [
+            "gzip, deflate"
+          ],
+          "Connection": [
+            "keep-alive"
+          ],
+          "User-Agent": [
+            "Postmarker/0.7.0.dev0"
+          ],
+          "X-Postmark-Server-Token": [
+            "SERVER_TOKEN"
+          ]
+        },
+        "method": "GET",
+        "uri": "https://api.postmarkapp.com/stats/outbound/opens/readtimes?fromdate=2017-01-30"
+      },
+      "response": {
+        "body": {
+          "base64_string": "H4sIAAAAAAAEAKtWckmsLFayiq4GMkpSlayUjAwMzXUNDHWNDZR0lAyAUoY6SoZmELo0LzsvvzwPyK7VwdRgCNRgZFCsrWRlpKNkUgymLCAaLSE8uH4zDP1GQCOUkFQY18ZiWE+k4YYGtQCghepl1wAAAA==",
+          "encoding": "utf-8"
+        },
+        "headers": {
+          "Cache-Control": [
+            "private"
+          ],
+          "Content-Encoding": [
+            "gzip"
+          ],
+          "Content-Length": [
+            "115"
+          ],
+          "Content-Type": [
+            "application/json; charset=utf-8"
+          ],
+          "Date": [
+            "Wed, 01 Feb 2017 08:29:24 GMT"
+          ],
+          "Server": [
+            "unicorns-double-rainbow"
+          ],
+          "X-Powered-By": [
+            "ASP.NET"
+          ]
+        },
+        "status": {
+          "code": 200,
+          "message": "OK"
+        },
+        "url": "https://api.postmarkapp.com/stats/outbound/opens/readtimes?fromdate=2017-01-30"
+      }
+    },
+    {
+      "recorded_at": "2017-02-01T08:29:25",
+      "request": {
+        "body": {
+          "base64_string": "",
+          "encoding": "utf-8"
+        },
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Accept-Encoding": [
+            "gzip, deflate"
+          ],
+          "Connection": [
+            "keep-alive"
+          ],
+          "User-Agent": [
+            "Postmarker/0.7.0.dev0"
+          ],
+          "X-Postmark-Server-Token": [
+            "SERVER_TOKEN"
+          ]
+        },
+        "method": "GET",
+        "uri": "https://api.postmarkapp.com/stats/outbound/clicks"
+      },
+      "response": {
+        "body": {
+          "base64_string": "eyJEYXlzIjpbXX0=",
+          "encoding": "utf-8"
+        },
+        "headers": {
+          "Cache-Control": [
+            "private"
+          ],
+          "Content-Length": [
+            "11"
+          ],
+          "Content-Type": [
+            "application/json; charset=utf-8"
+          ],
+          "Date": [
+            "Wed, 01 Feb 2017 08:29:24 GMT"
+          ],
+          "Server": [
+            "unicorns-double-rainbow"
+          ],
+          "X-Powered-By": [
+            "ASP.NET"
+          ]
+        },
+        "status": {
+          "code": 200,
+          "message": "OK"
+        },
+        "url": "https://api.postmarkapp.com/stats/outbound/clicks"
+      }
+    },
+    {
+      "recorded_at": "2017-02-01T08:29:25",
+      "request": {
+        "body": {
+          "base64_string": "",
+          "encoding": "utf-8"
+        },
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Accept-Encoding": [
+            "gzip, deflate"
+          ],
+          "Connection": [
+            "keep-alive"
+          ],
+          "User-Agent": [
+            "Postmarker/0.7.0.dev0"
+          ],
+          "X-Postmark-Server-Token": [
+            "SERVER_TOKEN"
+          ]
+        },
+        "method": "GET",
+        "uri": "https://api.postmarkapp.com/stats/outbound/clicks/browserfamilies"
+      },
+      "response": {
+        "body": {
+          "base64_string": "eyJEYXlzIjpbXX0=",
+          "encoding": "utf-8"
+        },
+        "headers": {
+          "Cache-Control": [
+            "private"
+          ],
+          "Content-Length": [
+            "11"
+          ],
+          "Content-Type": [
+            "application/json; charset=utf-8"
+          ],
+          "Date": [
+            "Wed, 01 Feb 2017 08:29:24 GMT"
+          ],
+          "Server": [
+            "unicorns-double-rainbow"
+          ],
+          "X-Powered-By": [
+            "ASP.NET"
+          ]
+        },
+        "status": {
+          "code": 200,
+          "message": "OK"
+        },
+        "url": "https://api.postmarkapp.com/stats/outbound/clicks/browserfamilies"
+      }
+    },
+    {
+      "recorded_at": "2017-02-01T08:29:25",
+      "request": {
+        "body": {
+          "base64_string": "",
+          "encoding": "utf-8"
+        },
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Accept-Encoding": [
+            "gzip, deflate"
+          ],
+          "Connection": [
+            "keep-alive"
+          ],
+          "User-Agent": [
+            "Postmarker/0.7.0.dev0"
+          ],
+          "X-Postmark-Server-Token": [
+            "SERVER_TOKEN"
+          ]
+        },
+        "method": "GET",
+        "uri": "https://api.postmarkapp.com/stats/outbound/clicks/platforms"
+      },
+      "response": {
+        "body": {
+          "base64_string": "eyJEYXlzIjpbXX0=",
+          "encoding": "utf-8"
+        },
+        "headers": {
+          "Cache-Control": [
+            "private"
+          ],
+          "Content-Length": [
+            "11"
+          ],
+          "Content-Type": [
+            "application/json; charset=utf-8"
+          ],
+          "Date": [
+            "Wed, 01 Feb 2017 08:29:25 GMT"
+          ],
+          "Server": [
+            "unicorns-double-rainbow"
+          ],
+          "X-Powered-By": [
+            "ASP.NET"
+          ]
+        },
+        "status": {
+          "code": 200,
+          "message": "OK"
+        },
+        "url": "https://api.postmarkapp.com/stats/outbound/clicks/platforms"
+      }
+    },
+    {
+      "recorded_at": "2017-02-01T08:29:25",
+      "request": {
+        "body": {
+          "base64_string": "",
+          "encoding": "utf-8"
+        },
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Accept-Encoding": [
+            "gzip, deflate"
+          ],
+          "Connection": [
+            "keep-alive"
+          ],
+          "User-Agent": [
+            "Postmarker/0.7.0.dev0"
+          ],
+          "X-Postmark-Server-Token": [
+            "SERVER_TOKEN"
+          ]
+        },
+        "method": "GET",
+        "uri": "https://api.postmarkapp.com/stats/outbound/clicks/location"
+      },
+      "response": {
+        "body": {
+          "base64_string": "eyJEYXlzIjpbXX0=",
+          "encoding": "utf-8"
+        },
+        "headers": {
+          "Cache-Control": [
+            "private"
+          ],
+          "Content-Length": [
+            "11"
+          ],
+          "Content-Type": [
+            "application/json; charset=utf-8"
+          ],
+          "Date": [
+            "Wed, 01 Feb 2017 08:29:25 GMT"
+          ],
+          "Server": [
+            "unicorns-double-rainbow"
+          ],
+          "X-Powered-By": [
+            "ASP.NET"
+          ]
+        },
+        "status": {
+          "code": 200,
+          "message": "OK"
+        },
+        "url": "https://api.postmarkapp.com/stats/outbound/clicks/location"
+      }
+    }
+  ],
+  "recorded_with": "betamax/0.8.0"
+}
\ No newline at end of file
diff --git a/tests/models/test_stats.py b/tests/models/test_stats.py
new file mode 100644
index 0000000..4e2a1e2
--- /dev/null
+++ b/tests/models/test_stats.py
@@ -0,0 +1,138 @@
+# coding: utf-8
+import pytest
+
+
+CASSETTE_NAME = 'stats'
+
+
+@pytest.mark.parametrize('method, kwargs, expected', (
+    (
+        'overview',
+        {'fromdate': '2017-01-30'},
+        {
+            'BounceRate': 0.0,
+            'Bounced': 0,
+            'Opens': 50,
+            'SMTPApiErrors': 0,
+            'Sent': 93,
+            'SpamComplaints': 0,
+            'SpamComplaintsRate': 0.0,
+            'TotalClicks': 0,
+            'TotalTrackedLinksSent': 0,
+            'Tracked': 93,
+            'UniqueLinksClicked': 0,
+            'UniqueOpens': 19,
+            'WithClientRecorded': 13,
+            'WithLinkTracking': 0,
+            'WithOpenTracking': 93,
+            'WithPlatformRecorded': 19,
+            'WithReadTimeRecorded': 19
+        }
+    ),
+    (
+        'sends',
+        {'fromdate': '2017-01-30'},
+        {
+            'Days': [
+                {'Date': '2017-01-30', 'Sent': 37},
+                {'Date': '2017-01-31', 'Sent': 54},
+                {'Date': '2017-02-01', 'Sent': 2}
+            ],
+            'Sent': 93
+        }
+    ),
+    (
+        'bounces',
+        {'fromdate': '2017-01-30'},
+        {'Days': []}
+    ),
+    (
+        'spam',
+        {'fromdate': '2017-01-30'},
+        {'Days': []}
+    ),
+    (
+        'tracked',
+        {'fromdate': '2017-01-30'},
+        {
+             'Days': [
+                 {'Date': '2017-01-30', 'Tracked': 37},
+                 {'Date': '2017-01-31', 'Tracked': 54},
+                 {'Date': '2017-02-01', 'Tracked': 2}
+             ],
+             'Tracked': 93
+         }
+    ),
+    (
+        'opens',
+        {'fromdate': '2017-01-30'},
+        {
+             'Days': [
+                 {'Date': '2017-01-30', 'Opens': 6, 'Unique': 3},
+                 {'Date': '2017-01-31', 'Opens': 39, 'Unique': 13},
+                 {'Date': '2017-02-01', 'Opens': 5, 'Unique': 3}
+             ],
+             'Opens': 50,
+             'Unique': 19
+         }
+    ),
+    (
+        'opens_platforms',
+        {'fromdate': '2017-01-30'},
+        {
+             'Days': [
+                 {'Date': '2017-01-30', 'Desktop': 1, 'Mobile': 1, 'Unknown': 1},
+                 {'Date': '2017-01-31', 'Desktop': 2, 'Unknown': 11},
+                 {'Date': '2017-02-01', 'Unknown': 3}
+             ],
+             'Desktop': 3,
+             'Mobile': 1,
+             'Unknown': 15
+         }
+    ),
+    (
+        'emailclients',
+        {'fromdate': '2017-01-30'},
+        {
+             'Days': [
+                 {'Date': '2017-01-30', 'Gmail': 1, 'Thunderbird': 1},
+                 {'Date': '2017-01-31', 'Gmail': 6, 'Thunderbird': 2},
+                 {'Date': '2017-02-01', 'Gmail': 3}
+             ],
+             'Gmail': 10,
+             'Thunderbird': 3
+         }
+    ),
+    (
+        'readtimes',
+        {'fromdate': '2017-01-30'},
+        {
+            'Days': [
+                 {'Date': '2017-01-30', '0s': 1, '16s': 1, 'unknown': 1},
+                 {'Date': '2017-01-31', '20s+': 2, '4s': 2, '8s': 1, '9s': 2, 'unknown': 6},
+                 {'Date': '2017-02-01', 'unknown': 3}
+            ],
+            '0s': 1,
+            '16s': 1,
+            '20s+': 2,
+            '4s': 2,
+            '8s': 1,
+            '9s': 2,
+            'unknown': 10
+        }
+    ),
+    (
+        'clicks', {}, {'Days': []}
+    ),
+    (
+        'browserfamilies', {}, {'Days': []}
+    ),
+    (
+        'clicks_platforms', {}, {'Days': []}
+    ),
+    (
+        'location', {}, {'Days': []}
+    ),
+))
+def test_methods(postmark, method, kwargs, expected):
+    assert getattr(postmark.stats, method)(**kwargs) == expected