-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgetresponse_client.py
More file actions
377 lines (301 loc) · 12.6 KB
/
getresponse_client.py
File metadata and controls
377 lines (301 loc) · 12.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
#!/usr/bin/env python3
"""
GetResponse API Integration Class
================================
A comprehensive Python class for interacting with the GetResponse API v3.
This class provides methods to manage contacts, campaigns, and perform
various email marketing operations.
Features:
- Add contacts to campaigns/lists
- Remove contacts from campaigns
- Search and manage contacts
- Get campaign information
- Error handling and logging
- Environment variable integration
Requirements:
- Python 3.6+
- requests library
- python-dotenv for environment variables
Setup:
1. Install required packages: pip install requests python-dotenv
2. Create a .env file with: GETRESPONSE_API_KEY=your_api_key_here
3. Import and use the GetResponseClient class
Author: Agent Zero
Date: 2025-11-06
"""
import os
import json
import requests
from typing import Dict, List, Optional, Union
from dotenv import load_dotenv
class GetResponseClient:
"""
A versatile client for interacting with the GetResponse API v3.
This class provides comprehensive methods to manage contacts, campaigns,
and perform email marketing operations through the GetResponse API.
Attributes:
base_url (str): The base URL for GetResponse API
api_key (str): The API key for authentication
headers (dict): HTTP headers including authentication
"""
def __init__(self, api_key: Optional[str] = None, base_url: str = "https://api.getresponse.com/v3"):
"""
Initialize the GetResponse client.
Args:
api_key (str, optional): GetResponse API key. If not provided,
will attempt to load from environment variable GETRESPONSE_API_KEY.
base_url (str): Base URL for GetResponse API. Defaults to retail API.
For MAX platform users, use appropriate URL.
Raises:
ValueError: If API key is not provided and not found in environment.
"""
load_dotenv() # Load environment variables from .env file
self.base_url = base_url.rstrip('/')
self.api_key = api_key or os.getenv('GETRESPONSE_API_KEY')
if not self.api_key:
raise ValueError(
"API key is required. Either pass it directly or set GETRESPONSE_API_KEY "
"environment variable or in .env file."
)
self.headers = {
'X-Auth-Token': f'api-key {self.api_key}',
'Content-Type': 'application/json'
}
def _make_request(
self,
method: str,
endpoint: str,
data: Optional[Dict] = None,
params: Optional[Dict] = None
) -> Dict:
"""
Make HTTP request to GetResponse API (v3).
Handles empty 202/204 responses gracefully.
"""
url = f"{self.base_url}{endpoint}"
try:
response = requests.request(
method=method,
url=url,
headers=self.headers,
json=data,
params=params,
timeout=30
)
# Erfolgreiche Antworten ohne Body akzeptieren
if response.status_code in (200, 201, 202, 204):
if not response.text.strip():
return {"success": True, "code": response.status_code, "message": "No content"}
try:
return response.json()
except ValueError:
# Kein valider JSON-Body, aber trotzdem Erfolg
return {"success": True, "code": response.status_code, "raw": response.text}
# Bekannte Fehlercodes mit spezifischer Meldung
if response.status_code == 400:
raise ValueError(f"Bad Request: {response.text}")
if response.status_code == 401:
raise ValueError("Unauthorized: Invalid API key")
if response.status_code == 404:
raise ValueError(f"Resource not found: {endpoint}")
if response.status_code == 429:
raise ValueError("Rate limit exceeded: Too many requests")
# Alle anderen Statuscodes
try:
err = response.json()
except ValueError:
err = response.text
raise ValueError(f"API Error {response.status_code}: {err}")
except requests.exceptions.Timeout:
raise ValueError("Request timeout: API took too long to respond")
except requests.exceptions.ConnectionError:
raise ValueError("Connection error: Unable to connect to GetResponse API")
except requests.exceptions.RequestException as e:
raise ValueError(f"Request failed: {e}")
def get_campaigns(self) -> List[Dict]:
"""
Get all campaigns (lists) from GetResponse account.
Returns:
List[Dict]: List of campaign objects with details
Example:
>>> client = GetResponseClient(api_key='your_key')
>>> campaigns = client.get_campaigns()
>>> for campaign in campaigns:
... print(f"{campaign['name']}: {campaign['campaignId']}")
"""
response = self._make_request('GET', '/campaigns')
return response
def get_campaign_by_id(self, campaign_id: str) -> Dict:
"""
Retrieve detailed information about a specific campaign by its ID.
Args:
campaign_id (str): Unique campaign ID (e.g., "MV2gN")
Returns:
dict: Campaign details including subscription settings, fromField, replyTo, etc.
Raises:
ValueError: If campaign not found or API error occurs
"""
if not campaign_id:
raise ValueError("Campaign ID is required")
endpoint = f"/campaigns/{campaign_id}"
response = self._make_request("GET", endpoint)
# Validate structure
if not isinstance(response, dict) or "campaignId" not in response:
raise ValueError(f"Invalid campaign response for ID {campaign_id}: {response}")
return response
def get_campaign_by_name(self, campaign_name: str) -> Optional[Dict]:
"""
Get a specific campaign by name.
Args:
campaign_name (str): Name of the campaign to find
Returns:
dict: Campaign object if found, None otherwise
"""
campaigns = self.get_campaigns()
for campaign in campaigns:
if campaign.get('name') == campaign_name:
return campaign
return None
def add_contact(self, email: str, campaign_id: str, name: Optional[str] = None,
day_of_cycle: Optional[int] = None, custom_fields: Optional[Dict] = None) -> Dict:
"""
Add a contact to a specific campaign (list).
Args:
email (str): Contact's email address
campaign_id (str): ID of the campaign to add contact to
name (str, optional): Contact's name
day_of_cycle (int, optional): Day in autoresponder cycle
custom_fields (dict, optional): Custom field values
Returns:
dict: Response from API with contact details
Example:
>>> client = GetResponseClient(api_key='your_key')
>>> result = client.add_contact(
... email='john@example.com',
... campaign_id='abc123',
... name='John Doe',
... day_of_cycle=0
... )
"""
data = {
'email': email,
'campaign': {'campaignId': campaign_id}
}
if name:
data['name'] = name
if day_of_cycle is not None:
data['dayOfCycle'] = day_of_cycle
if custom_fields:
data['customFieldValues'] = [
{'customFieldId': field_id, 'value': [value]}
for field_id, value in custom_fields.items()
]
return self._make_request('POST', '/contacts', data=data)
def get_contact(self, contact_id: str) -> Dict:
"""
Get contact details by ID.
Args:
contact_id (str): ID of the contact to retrieve
Returns:
dict: Contact object with details
"""
return self._make_request('GET', f'/contacts/{contact_id}')
def search_contacts(self, query: Optional[str] = None, campaign_id: Optional[str] = None,
email: Optional[str] = None, limit: int = 100) -> List[Dict]:
"""
Search for contacts based on various criteria.
Args:
query (str, optional): Search query for contact name or email
campaign_id (str, optional): Filter by campaign ID
email (str, optional): Filter by exact email address
limit (int): Maximum number of results to return
Returns:
List[Dict]: List of matching contacts
"""
params = {'perPage': limit}
if query:
params['query[name]'] = query
params['query[email]'] = query
if campaign_id:
params['query[campaignId]'] = campaign_id
if email:
params['query[email]'] = email
return self._make_request('GET', '/contacts', params=params)
def get_contact_by_email(self, email: str, campaign_id: Optional[str] = None) -> Optional[Dict]:
"""
Get a specific contact by email address.
Args:
email (str): Email address to search for
campaign_id (str, optional): Optional campaign filter
Returns:
dict: Contact object if found, None otherwise
"""
contacts = self.search_contacts(email=email, campaign_id=campaign_id, limit=1)
return contacts[0] if contacts else None
def remove_contact(self, contact_id: str) -> Dict:
"""
Remove (delete) a contact from GetResponse.
Args:
contact_id (str): ID of the contact to remove
Returns:
dict: Success message
Example:
>>> client = GetResponseClient(api_key='your_key')
>>> result = client.remove_contact('contact123')
"""
return self._make_request('DELETE', f'/contacts/{contact_id}')
def remove_contact_by_email(self, email: str, campaign_id: Optional[str] = None) -> bool:
"""
Remove a contact by email address.
Args:
email (str): Email address of contact to remove
campaign_id (str, optional): Optional campaign filter
Returns:
bool: True if contact was found and removed, False otherwise
"""
contact = self.get_contact_by_email(email, campaign_id)
if contact:
self.remove_contact(contact['contactId'])
return True
return False
def update_contact(self, contact_id: str, name: Optional[str] = None,
campaign_id: Optional[str] = None, custom_fields: Optional[Dict] = None) -> Dict:
"""
Update contact information.
Args:
contact_id (str): ID of contact to update
name (str, optional): New name for contact
campaign_id (str, optional): New campaign ID
custom_fields (dict, optional): Updated custom fields
Returns:
dict: Updated contact object
"""
data = {}
if name:
data['name'] = name
if campaign_id:
data['campaign'] = {'campaignId': campaign_id}
if custom_fields:
data['customFieldValues'] = [
{'customFieldId': field_id, 'value': [value]}
for field_id, value in custom_fields.items()
]
return self._make_request('POST', f'/contacts/{contact_id}', data=data)
def get_account_info(self) -> Dict:
"""
Get account information and limits.
Returns:
dict: Account details and usage information
"""
return self._make_request('GET', '/accounts')
def test_connection(self) -> bool:
"""
Test API connection and authentication.
Returns:
bool: True if connection successful, False otherwise
"""
try:
self.get_account_info()
return True
except Exception:
return False