Skip to content

Commit f0f6a05

Browse files
piobenyclaude
andcommitted
Add Stats API with get, by_domains, by_categories, by_email_service_providers, by_date endpoints
Also adds api_query_params to RequestParams for automatic [] serialization of list query params. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 8821498 commit f0f6a05

7 files changed

Lines changed: 465 additions & 0 deletions

File tree

examples/general/stats.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import mailtrap as mt
2+
from mailtrap.models.stats import SendingStatGroup
3+
from mailtrap.models.stats import SendingStats
4+
from mailtrap.models.stats import StatsFilterParams
5+
6+
API_TOKEN = "YOUR_API_TOKEN"
7+
ACCOUNT_ID = "YOUR_ACCOUNT_ID"
8+
9+
client = mt.MailtrapClient(token=API_TOKEN)
10+
stats_api = client.general_api.stats
11+
12+
13+
def get_stats(account_id: int) -> SendingStats:
14+
params = StatsFilterParams(start_date="2026-01-01", end_date="2026-01-31")
15+
return stats_api.get(account_id=account_id, params=params)
16+
17+
18+
def get_stats_by_domains(account_id: int) -> list[SendingStatGroup]:
19+
params = StatsFilterParams(start_date="2026-01-01", end_date="2026-01-31")
20+
return stats_api.by_domains(account_id=account_id, params=params)
21+
22+
23+
def get_stats_by_categories(account_id: int) -> list[SendingStatGroup]:
24+
params = StatsFilterParams(start_date="2026-01-01", end_date="2026-01-31")
25+
return stats_api.by_categories(account_id=account_id, params=params)
26+
27+
28+
def get_stats_by_email_service_providers(account_id: int) -> list[SendingStatGroup]:
29+
params = StatsFilterParams(start_date="2026-01-01", end_date="2026-01-31")
30+
return stats_api.by_email_service_providers(account_id=account_id, params=params)
31+
32+
33+
def get_stats_by_date(account_id: int) -> list[SendingStatGroup]:
34+
params = StatsFilterParams(start_date="2026-01-01", end_date="2026-01-31")
35+
return stats_api.by_date(account_id=account_id, params=params)
36+
37+
38+
def get_stats_with_filters(account_id: int) -> SendingStats:
39+
params = StatsFilterParams(
40+
start_date="2026-01-01",
41+
end_date="2026-01-31",
42+
sending_domain_ids=[1, 2],
43+
sending_streams=["transactional"],
44+
categories=["Transactional", "Marketing"],
45+
email_service_providers=["Gmail", "Yahoo"],
46+
)
47+
return stats_api.get(account_id=account_id, params=params)
48+
49+
50+
def get_stats_by_domains_with_filters(account_id: int) -> list[SendingStatGroup]:
51+
params = StatsFilterParams(
52+
start_date="2026-01-01",
53+
end_date="2026-01-31",
54+
sending_streams=["transactional"],
55+
categories=["Transactional"],
56+
)
57+
return stats_api.by_domains(account_id=account_id, params=params)
58+
59+
60+
if __name__ == "__main__":
61+
print(get_stats(ACCOUNT_ID))
62+
print(get_stats_by_domains(ACCOUNT_ID))
63+
print(get_stats_by_categories(ACCOUNT_ID))
64+
print(get_stats_by_email_service_providers(ACCOUNT_ID))
65+
print(get_stats_by_date(ACCOUNT_ID))
66+
print(get_stats_with_filters(ACCOUNT_ID))
67+
print(get_stats_by_domains_with_filters(ACCOUNT_ID))

mailtrap/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,6 @@
3232
from .models.projects import ProjectParams
3333
from .models.sending_domains import CreateSendingDomainParams
3434
from .models.sending_domains import SendSetupInstructionsParams
35+
from .models.stats import StatsFilterParams
3536
from .models.templates import CreateEmailTemplateParams
3637
from .models.templates import UpdateEmailTemplateParams

mailtrap/api/general.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from mailtrap.api.resources.accounts import AccountsApi
33
from mailtrap.api.resources.billing import BillingApi
44
from mailtrap.api.resources.permissions import PermissionsApi
5+
from mailtrap.api.resources.stats import StatsApi
56
from mailtrap.http import HttpClient
67

78

@@ -24,3 +25,7 @@ def billing(self) -> BillingApi:
2425
@property
2526
def permissions(self) -> PermissionsApi:
2627
return PermissionsApi(client=self._client)
28+
29+
@property
30+
def stats(self) -> StatsApi:
31+
return StatsApi(client=self._client)

mailtrap/api/resources/stats.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
from mailtrap.http import HttpClient
2+
from mailtrap.models.stats import SendingStatGroup
3+
from mailtrap.models.stats import SendingStats
4+
from mailtrap.models.stats import StatsFilterParams
5+
6+
GROUP_KEYS = {
7+
"domains": "sending_domain_id",
8+
"categories": "category",
9+
"email_service_providers": "email_service_provider",
10+
"date": "date",
11+
}
12+
13+
14+
class StatsApi:
15+
def __init__(self, client: HttpClient) -> None:
16+
self._client = client
17+
18+
def get(self, account_id: int, params: StatsFilterParams) -> SendingStats:
19+
"""Get aggregated sending stats."""
20+
response = self._client.get(self._base_path(account_id), params=params.api_query_params)
21+
return SendingStats(**response)
22+
23+
def by_domains(self, account_id: int, params: StatsFilterParams) -> list[SendingStatGroup]:
24+
"""Get sending stats grouped by domains."""
25+
return self._grouped_stats(account_id, "domains", params)
26+
27+
def by_categories(self, account_id: int, params: StatsFilterParams) -> list[SendingStatGroup]:
28+
"""Get sending stats grouped by categories."""
29+
return self._grouped_stats(account_id, "categories", params)
30+
31+
def by_email_service_providers(
32+
self, account_id: int, params: StatsFilterParams
33+
) -> list[SendingStatGroup]:
34+
"""Get sending stats grouped by email service providers."""
35+
return self._grouped_stats(account_id, "email_service_providers", params)
36+
37+
def by_date(self, account_id: int, params: StatsFilterParams) -> list[SendingStatGroup]:
38+
"""Get sending stats grouped by date."""
39+
return self._grouped_stats(account_id, "date", params)
40+
41+
def _grouped_stats(
42+
self, account_id: int, group: str, params: StatsFilterParams
43+
) -> list[SendingStatGroup]:
44+
response = self._client.get(
45+
f"{self._base_path(account_id)}/{group}", params=params.api_query_params
46+
)
47+
group_key = GROUP_KEYS[group]
48+
49+
return [
50+
SendingStatGroup(
51+
name=group_key,
52+
value=item[group_key],
53+
stats=SendingStats(**item["stats"]),
54+
)
55+
for item in response
56+
]
57+
58+
@staticmethod
59+
def _base_path(account_id: int) -> str:
60+
return f"/api/accounts/{account_id}/stats"

mailtrap/models/common.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@ def api_data(self: T) -> dict[str, Any]:
1818
TypeAdapter(type(self)).dump_python(self, by_alias=True, exclude_none=True),
1919
)
2020

21+
@property
22+
def api_query_params(self: T) -> dict[str, Any]:
23+
data = self.api_data
24+
for key, value in list(data.items()):
25+
if isinstance(value, list):
26+
data[f"{key}[]"] = data.pop(key)
27+
return data
28+
2129

2230
@dataclass
2331
class DeletedObject:

mailtrap/models/stats.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
from typing import Optional
2+
from typing import Union
3+
4+
from pydantic.dataclasses import dataclass
5+
6+
from mailtrap.models.common import RequestParams
7+
8+
9+
@dataclass
10+
class SendingStats:
11+
delivery_count: int
12+
delivery_rate: float
13+
bounce_count: int
14+
bounce_rate: float
15+
open_count: int
16+
open_rate: float
17+
click_count: int
18+
click_rate: float
19+
spam_count: int
20+
spam_rate: float
21+
22+
23+
@dataclass
24+
class SendingStatGroup:
25+
name: str
26+
value: Union[str, int]
27+
stats: SendingStats
28+
29+
30+
@dataclass
31+
class StatsFilterParams(RequestParams):
32+
start_date: str = ""
33+
end_date: str = ""
34+
sending_domain_ids: Optional[list] = None
35+
sending_streams: Optional[list] = None
36+
categories: Optional[list] = None
37+
email_service_providers: Optional[list] = None

0 commit comments

Comments
 (0)