foxreach 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,46 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ build:
9
+ name: Build distribution
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+
14
+ - name: Set up Python
15
+ uses: actions/setup-python@v5
16
+ with:
17
+ python-version: "3.12"
18
+
19
+ - name: Install build tools
20
+ run: pip install build
21
+
22
+ - name: Build package
23
+ run: python -m build
24
+
25
+ - name: Upload dist artifacts
26
+ uses: actions/upload-artifact@v4
27
+ with:
28
+ name: dist
29
+ path: dist/
30
+
31
+ publish:
32
+ name: Publish to PyPI
33
+ needs: build
34
+ runs-on: ubuntu-latest
35
+ environment: pypi
36
+ permissions:
37
+ id-token: write
38
+ steps:
39
+ - name: Download dist artifacts
40
+ uses: actions/download-artifact@v4
41
+ with:
42
+ name: dist
43
+ path: dist/
44
+
45
+ - name: Publish to PyPI
46
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,6 @@
1
+ dist/
2
+ build/
3
+ *.egg-info/
4
+ __pycache__/
5
+ *.pyc
6
+ .venv/
foxreach-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 FoxReach
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,182 @@
1
+ Metadata-Version: 2.4
2
+ Name: foxreach
3
+ Version: 0.1.0
4
+ Summary: Official Python SDK for the FoxReach cold email outreach API
5
+ Project-URL: Homepage, https://foxreach.io
6
+ Project-URL: Documentation, https://docs.foxreach.io/sdks/python
7
+ Project-URL: Repository, https://github.com/foxreach/foxreach-python-sdk
8
+ Project-URL: Changelog, https://docs.foxreach.io/changelog
9
+ Author-email: FoxReach <dev@foxreach.io>
10
+ License: MIT
11
+ License-File: LICENSE
12
+ Keywords: api,cold-email,email,foxreach,outreach,sdk
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Communications :: Email
23
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.9
26
+ Requires-Dist: httpx<1.0,>=0.25.0
27
+ Provides-Extra: dev
28
+ Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
29
+ Requires-Dist: pytest>=7.0; extra == 'dev'
30
+ Requires-Dist: respx>=0.20; extra == 'dev'
31
+ Description-Content-Type: text/markdown
32
+
33
+ # FoxReach Python SDK
34
+
35
+ Official Python SDK for the [FoxReach](https://foxreach.io) cold email outreach API.
36
+
37
+ ## Installation
38
+
39
+ ```bash
40
+ pip install foxreach
41
+ ```
42
+
43
+ ## Quick Start
44
+
45
+ ```python
46
+ from foxreach import FoxReach, LeadCreate
47
+
48
+ client = FoxReach(api_key="otr_your_api_key")
49
+
50
+ # Create a lead
51
+ lead = client.leads.create(LeadCreate(
52
+ email="jane@example.com",
53
+ first_name="Jane",
54
+ last_name="Smith",
55
+ company="Acme Corp",
56
+ ))
57
+
58
+ # List leads with pagination
59
+ page = client.leads.list(search="jane", page_size=25)
60
+ for lead in page:
61
+ print(lead.email)
62
+
63
+ # Auto-paginate through all results
64
+ for lead in client.leads.list().auto_paging_iter():
65
+ print(lead.email)
66
+ ```
67
+
68
+ ## Async Support
69
+
70
+ ```python
71
+ from foxreach import AsyncFoxReach, LeadCreate
72
+
73
+ async with AsyncFoxReach(api_key="otr_your_api_key") as client:
74
+ lead = await client.leads.create(LeadCreate(email="jane@example.com"))
75
+
76
+ async for lead in (await client.leads.list()).auto_paging_iter():
77
+ print(lead.email)
78
+ ```
79
+
80
+ ## Resources
81
+
82
+ ### Leads
83
+
84
+ ```python
85
+ client.leads.list(page=1, page_size=50, search="...", status="active", tags="...")
86
+ client.leads.get("cld_...")
87
+ client.leads.create(LeadCreate(email="..."))
88
+ client.leads.update("cld_...", LeadUpdate(company="New Co"))
89
+ client.leads.delete("cld_...")
90
+ ```
91
+
92
+ ### Campaigns
93
+
94
+ ```python
95
+ client.campaigns.list(status="active")
96
+ client.campaigns.get("cmp_...")
97
+ client.campaigns.create(CampaignCreate(name="Q1 Outreach"))
98
+ client.campaigns.update("cmp_...", CampaignUpdate(daily_limit=100))
99
+ client.campaigns.delete("cmp_...")
100
+ client.campaigns.start("cmp_...")
101
+ client.campaigns.pause("cmp_...")
102
+ client.campaigns.add_leads("cmp_...", ["cld_1", "cld_2"])
103
+ client.campaigns.add_accounts("cmp_...", ["acc_1"])
104
+ ```
105
+
106
+ ### Sequences
107
+
108
+ ```python
109
+ client.campaigns.sequences.list("cmp_...")
110
+ client.campaigns.sequences.create("cmp_...", SequenceCreate(body="Hi {{firstName}}"))
111
+ client.campaigns.sequences.update("cmp_...", "seq_...", SequenceUpdate(delay_days=3))
112
+ client.campaigns.sequences.delete("cmp_...", "seq_...")
113
+ ```
114
+
115
+ ### Templates
116
+
117
+ ```python
118
+ client.templates.list()
119
+ client.templates.get("tpl_...")
120
+ client.templates.create(TemplateCreate(name="Follow-up", body="Hi {{firstName}}"))
121
+ client.templates.update("tpl_...", TemplateUpdate(name="New name"))
122
+ client.templates.delete("tpl_...")
123
+ ```
124
+
125
+ ### Email Accounts
126
+
127
+ ```python
128
+ client.email_accounts.list()
129
+ client.email_accounts.get("acc_...")
130
+ client.email_accounts.delete("acc_...")
131
+ ```
132
+
133
+ ### Inbox
134
+
135
+ ```python
136
+ client.inbox.list_threads(category="interested", is_read=False)
137
+ client.inbox.get("rpl_...")
138
+ client.inbox.update("rpl_...", ThreadUpdate(is_read=True))
139
+ ```
140
+
141
+ ### Analytics
142
+
143
+ ```python
144
+ overview = client.analytics.overview()
145
+ print(f"Reply rate: {overview.reply_rate}%")
146
+
147
+ stats = client.analytics.campaign("cmp_...")
148
+ print(f"Sent: {stats.sent}, Replied: {stats.replied}")
149
+ ```
150
+
151
+ ## Error Handling
152
+
153
+ ```python
154
+ from foxreach import FoxReach, NotFoundError, RateLimitError, AuthenticationError
155
+
156
+ client = FoxReach(api_key="otr_...")
157
+
158
+ try:
159
+ lead = client.leads.get("cld_nonexistent")
160
+ except NotFoundError:
161
+ print("Lead not found")
162
+ except RateLimitError as e:
163
+ print(f"Rate limited, retry after {e.retry_after}s")
164
+ except AuthenticationError:
165
+ print("Invalid API key")
166
+ ```
167
+
168
+ ## Configuration
169
+
170
+ ```python
171
+ client = FoxReach(
172
+ api_key="otr_...",
173
+ base_url="https://api.foxreach.io/api/v1", # default
174
+ timeout=30.0, # seconds
175
+ max_retries=3, # retries on 429
176
+ )
177
+ ```
178
+
179
+ ## Requirements
180
+
181
+ - Python 3.9+
182
+ - httpx
@@ -0,0 +1,150 @@
1
+ # FoxReach Python SDK
2
+
3
+ Official Python SDK for the [FoxReach](https://foxreach.io) cold email outreach API.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install foxreach
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```python
14
+ from foxreach import FoxReach, LeadCreate
15
+
16
+ client = FoxReach(api_key="otr_your_api_key")
17
+
18
+ # Create a lead
19
+ lead = client.leads.create(LeadCreate(
20
+ email="jane@example.com",
21
+ first_name="Jane",
22
+ last_name="Smith",
23
+ company="Acme Corp",
24
+ ))
25
+
26
+ # List leads with pagination
27
+ page = client.leads.list(search="jane", page_size=25)
28
+ for lead in page:
29
+ print(lead.email)
30
+
31
+ # Auto-paginate through all results
32
+ for lead in client.leads.list().auto_paging_iter():
33
+ print(lead.email)
34
+ ```
35
+
36
+ ## Async Support
37
+
38
+ ```python
39
+ from foxreach import AsyncFoxReach, LeadCreate
40
+
41
+ async with AsyncFoxReach(api_key="otr_your_api_key") as client:
42
+ lead = await client.leads.create(LeadCreate(email="jane@example.com"))
43
+
44
+ async for lead in (await client.leads.list()).auto_paging_iter():
45
+ print(lead.email)
46
+ ```
47
+
48
+ ## Resources
49
+
50
+ ### Leads
51
+
52
+ ```python
53
+ client.leads.list(page=1, page_size=50, search="...", status="active", tags="...")
54
+ client.leads.get("cld_...")
55
+ client.leads.create(LeadCreate(email="..."))
56
+ client.leads.update("cld_...", LeadUpdate(company="New Co"))
57
+ client.leads.delete("cld_...")
58
+ ```
59
+
60
+ ### Campaigns
61
+
62
+ ```python
63
+ client.campaigns.list(status="active")
64
+ client.campaigns.get("cmp_...")
65
+ client.campaigns.create(CampaignCreate(name="Q1 Outreach"))
66
+ client.campaigns.update("cmp_...", CampaignUpdate(daily_limit=100))
67
+ client.campaigns.delete("cmp_...")
68
+ client.campaigns.start("cmp_...")
69
+ client.campaigns.pause("cmp_...")
70
+ client.campaigns.add_leads("cmp_...", ["cld_1", "cld_2"])
71
+ client.campaigns.add_accounts("cmp_...", ["acc_1"])
72
+ ```
73
+
74
+ ### Sequences
75
+
76
+ ```python
77
+ client.campaigns.sequences.list("cmp_...")
78
+ client.campaigns.sequences.create("cmp_...", SequenceCreate(body="Hi {{firstName}}"))
79
+ client.campaigns.sequences.update("cmp_...", "seq_...", SequenceUpdate(delay_days=3))
80
+ client.campaigns.sequences.delete("cmp_...", "seq_...")
81
+ ```
82
+
83
+ ### Templates
84
+
85
+ ```python
86
+ client.templates.list()
87
+ client.templates.get("tpl_...")
88
+ client.templates.create(TemplateCreate(name="Follow-up", body="Hi {{firstName}}"))
89
+ client.templates.update("tpl_...", TemplateUpdate(name="New name"))
90
+ client.templates.delete("tpl_...")
91
+ ```
92
+
93
+ ### Email Accounts
94
+
95
+ ```python
96
+ client.email_accounts.list()
97
+ client.email_accounts.get("acc_...")
98
+ client.email_accounts.delete("acc_...")
99
+ ```
100
+
101
+ ### Inbox
102
+
103
+ ```python
104
+ client.inbox.list_threads(category="interested", is_read=False)
105
+ client.inbox.get("rpl_...")
106
+ client.inbox.update("rpl_...", ThreadUpdate(is_read=True))
107
+ ```
108
+
109
+ ### Analytics
110
+
111
+ ```python
112
+ overview = client.analytics.overview()
113
+ print(f"Reply rate: {overview.reply_rate}%")
114
+
115
+ stats = client.analytics.campaign("cmp_...")
116
+ print(f"Sent: {stats.sent}, Replied: {stats.replied}")
117
+ ```
118
+
119
+ ## Error Handling
120
+
121
+ ```python
122
+ from foxreach import FoxReach, NotFoundError, RateLimitError, AuthenticationError
123
+
124
+ client = FoxReach(api_key="otr_...")
125
+
126
+ try:
127
+ lead = client.leads.get("cld_nonexistent")
128
+ except NotFoundError:
129
+ print("Lead not found")
130
+ except RateLimitError as e:
131
+ print(f"Rate limited, retry after {e.retry_after}s")
132
+ except AuthenticationError:
133
+ print("Invalid API key")
134
+ ```
135
+
136
+ ## Configuration
137
+
138
+ ```python
139
+ client = FoxReach(
140
+ api_key="otr_...",
141
+ base_url="https://api.foxreach.io/api/v1", # default
142
+ timeout=30.0, # seconds
143
+ max_retries=3, # retries on 429
144
+ )
145
+ ```
146
+
147
+ ## Requirements
148
+
149
+ - Python 3.9+
150
+ - httpx
@@ -0,0 +1,79 @@
1
+ """FoxReach — Official Python SDK for the FoxReach cold email outreach API."""
2
+
3
+ from .client import AsyncFoxReach, FoxReach
4
+ from ._exceptions import (
5
+ AuthenticationError,
6
+ BadRequestError,
7
+ ConnectionError,
8
+ FoxReachError,
9
+ NotFoundError,
10
+ PermissionError,
11
+ RateLimitError,
12
+ ServerError,
13
+ ValidationError,
14
+ )
15
+ from .types import (
16
+ Campaign,
17
+ CampaignAnalytics,
18
+ CampaignCreate,
19
+ CampaignUpdate,
20
+ DailyStat,
21
+ EmailAccount,
22
+ Lead,
23
+ LeadCreate,
24
+ LeadListParams,
25
+ LeadUpdate,
26
+ OverviewStats,
27
+ PaginationMeta,
28
+ Sequence,
29
+ SequenceCreate,
30
+ SequenceUpdate,
31
+ Template,
32
+ TemplateCreate,
33
+ TemplateUpdate,
34
+ Thread,
35
+ ThreadListParams,
36
+ ThreadUpdate,
37
+ WebhookEvent,
38
+ )
39
+
40
+ __version__ = "0.1.0"
41
+
42
+ __all__ = [
43
+ # Clients
44
+ "FoxReach",
45
+ "AsyncFoxReach",
46
+ # Exceptions
47
+ "FoxReachError",
48
+ "AuthenticationError",
49
+ "BadRequestError",
50
+ "ConnectionError",
51
+ "NotFoundError",
52
+ "PermissionError",
53
+ "RateLimitError",
54
+ "ServerError",
55
+ "ValidationError",
56
+ # Types
57
+ "Campaign",
58
+ "CampaignAnalytics",
59
+ "CampaignCreate",
60
+ "CampaignUpdate",
61
+ "DailyStat",
62
+ "EmailAccount",
63
+ "Lead",
64
+ "LeadCreate",
65
+ "LeadListParams",
66
+ "LeadUpdate",
67
+ "OverviewStats",
68
+ "PaginationMeta",
69
+ "Sequence",
70
+ "SequenceCreate",
71
+ "SequenceUpdate",
72
+ "Template",
73
+ "TemplateCreate",
74
+ "TemplateUpdate",
75
+ "Thread",
76
+ "ThreadListParams",
77
+ "ThreadUpdate",
78
+ "WebhookEvent",
79
+ ]
@@ -0,0 +1,58 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Optional
4
+
5
+
6
+ class FoxReachError(Exception):
7
+ """Base exception for all FoxReach SDK errors."""
8
+
9
+ def __init__(
10
+ self,
11
+ message: str,
12
+ status_code: Optional[int] = None,
13
+ response_body: Optional[dict[str, Any]] = None,
14
+ ) -> None:
15
+ super().__init__(message)
16
+ self.status_code = status_code
17
+ self.response_body = response_body
18
+
19
+
20
+ class AuthenticationError(FoxReachError):
21
+ """Raised on 401 Unauthorized responses."""
22
+
23
+
24
+ class PermissionError(FoxReachError):
25
+ """Raised on 403 Forbidden responses."""
26
+
27
+
28
+ class NotFoundError(FoxReachError):
29
+ """Raised on 404 Not Found responses."""
30
+
31
+
32
+ class BadRequestError(FoxReachError):
33
+ """Raised on 400 Bad Request responses."""
34
+
35
+
36
+ class ValidationError(FoxReachError):
37
+ """Raised on 422 Unprocessable Entity responses."""
38
+
39
+
40
+ class RateLimitError(FoxReachError):
41
+ """Raised on 429 Too Many Requests after retries are exhausted."""
42
+
43
+ def __init__(
44
+ self,
45
+ message: str,
46
+ retry_after: Optional[float] = None,
47
+ **kwargs: Any,
48
+ ) -> None:
49
+ super().__init__(message, **kwargs)
50
+ self.retry_after = retry_after
51
+
52
+
53
+ class ServerError(FoxReachError):
54
+ """Raised on 5xx server errors."""
55
+
56
+
57
+ class ConnectionError(FoxReachError):
58
+ """Raised on network / connection errors."""