chub-dev 0.1.0 → 0.1.2-beta.0
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.
- package/README.md +55 -0
- package/bin/chub-mcp +2 -0
- package/dist/airtable/docs/database/javascript/DOC.md +1437 -0
- package/dist/airtable/docs/database/python/DOC.md +1735 -0
- package/dist/amplitude/docs/analytics/javascript/DOC.md +1282 -0
- package/dist/amplitude/docs/analytics/python/DOC.md +1199 -0
- package/dist/anthropic/docs/claude-api/javascript/DOC.md +503 -0
- package/dist/anthropic/docs/claude-api/python/DOC.md +389 -0
- package/dist/asana/docs/tasks/DOC.md +1396 -0
- package/dist/assemblyai/docs/transcription/DOC.md +1043 -0
- package/dist/atlassian/docs/confluence/javascript/DOC.md +1347 -0
- package/dist/atlassian/docs/confluence/python/DOC.md +1604 -0
- package/dist/auth0/docs/identity/javascript/DOC.md +968 -0
- package/dist/auth0/docs/identity/python/DOC.md +1199 -0
- package/dist/aws/docs/s3/javascript/DOC.md +1773 -0
- package/dist/aws/docs/s3/python/DOC.md +1807 -0
- package/dist/binance/docs/trading/javascript/DOC.md +1315 -0
- package/dist/binance/docs/trading/python/DOC.md +1454 -0
- package/dist/braintree/docs/gateway/javascript/DOC.md +1278 -0
- package/dist/braintree/docs/gateway/python/DOC.md +1179 -0
- package/dist/chromadb/docs/embeddings-db/javascript/DOC.md +1263 -0
- package/dist/chromadb/docs/embeddings-db/python/DOC.md +1707 -0
- package/dist/clerk/docs/auth/javascript/DOC.md +1220 -0
- package/dist/clerk/docs/auth/python/DOC.md +274 -0
- package/dist/cloudflare/docs/workers/javascript/DOC.md +918 -0
- package/dist/cloudflare/docs/workers/python/DOC.md +994 -0
- package/dist/cockroachdb/docs/distributed-db/DOC.md +1500 -0
- package/dist/cohere/docs/llm/DOC.md +1335 -0
- package/dist/datadog/docs/monitoring/javascript/DOC.md +1740 -0
- package/dist/datadog/docs/monitoring/python/DOC.md +1815 -0
- package/dist/deepgram/docs/speech/javascript/DOC.md +885 -0
- package/dist/deepgram/docs/speech/python/DOC.md +685 -0
- package/dist/deepl/docs/translation/javascript/DOC.md +887 -0
- package/dist/deepl/docs/translation/python/DOC.md +944 -0
- package/dist/deepseek/docs/llm/DOC.md +1220 -0
- package/dist/directus/docs/headless-cms/javascript/DOC.md +1128 -0
- package/dist/directus/docs/headless-cms/python/DOC.md +1276 -0
- package/dist/discord/docs/bot/javascript/DOC.md +1090 -0
- package/dist/discord/docs/bot/python/DOC.md +1130 -0
- package/dist/elasticsearch/docs/search/DOC.md +1634 -0
- package/dist/elevenlabs/docs/text-to-speech/javascript/DOC.md +336 -0
- package/dist/elevenlabs/docs/text-to-speech/python/DOC.md +552 -0
- package/dist/firebase/docs/auth/DOC.md +1015 -0
- package/dist/gemini/docs/genai/javascript/DOC.md +691 -0
- package/dist/gemini/docs/genai/python/DOC.md +555 -0
- package/dist/github/docs/octokit/DOC.md +1560 -0
- package/dist/google/docs/bigquery/javascript/DOC.md +1688 -0
- package/dist/google/docs/bigquery/python/DOC.md +1503 -0
- package/dist/hubspot/docs/crm/javascript/DOC.md +1805 -0
- package/dist/hubspot/docs/crm/python/DOC.md +2033 -0
- package/dist/huggingface/docs/transformers/DOC.md +948 -0
- package/dist/intercom/docs/messaging/javascript/DOC.md +1844 -0
- package/dist/intercom/docs/messaging/python/DOC.md +1797 -0
- package/dist/jira/docs/issues/javascript/DOC.md +1420 -0
- package/dist/jira/docs/issues/python/DOC.md +1492 -0
- package/dist/kafka/docs/streaming/javascript/DOC.md +1671 -0
- package/dist/kafka/docs/streaming/python/DOC.md +1464 -0
- package/dist/landingai-ade/docs/api/DOC.md +620 -0
- package/dist/landingai-ade/docs/sdk/python/DOC.md +489 -0
- package/dist/landingai-ade/docs/sdk/typescript/DOC.md +542 -0
- package/dist/landingai-ade/skills/SKILL.md +489 -0
- package/dist/launchdarkly/docs/feature-flags/javascript/DOC.md +1191 -0
- package/dist/launchdarkly/docs/feature-flags/python/DOC.md +1671 -0
- package/dist/linear/docs/tracker/DOC.md +1554 -0
- package/dist/livekit/docs/realtime/javascript/DOC.md +303 -0
- package/dist/livekit/docs/realtime/python/DOC.md +163 -0
- package/dist/mailchimp/docs/marketing/DOC.md +1420 -0
- package/dist/meilisearch/docs/search/DOC.md +1241 -0
- package/dist/microsoft/docs/onedrive/javascript/DOC.md +1421 -0
- package/dist/microsoft/docs/onedrive/python/DOC.md +1549 -0
- package/dist/mongodb/docs/atlas/DOC.md +2041 -0
- package/dist/notion/docs/workspace-api/javascript/DOC.md +1435 -0
- package/dist/notion/docs/workspace-api/python/DOC.md +1400 -0
- package/dist/okta/docs/identity/javascript/DOC.md +1171 -0
- package/dist/okta/docs/identity/python/DOC.md +1401 -0
- package/dist/openai/docs/chat/javascript/DOC.md +407 -0
- package/dist/openai/docs/chat/python/DOC.md +568 -0
- package/dist/paypal/docs/checkout/DOC.md +278 -0
- package/dist/pinecone/docs/sdk/javascript/DOC.md +984 -0
- package/dist/pinecone/docs/sdk/python/DOC.md +1395 -0
- package/dist/plaid/docs/banking/javascript/DOC.md +1163 -0
- package/dist/plaid/docs/banking/python/DOC.md +1203 -0
- package/dist/playwright-community/skills/login-flows/SKILL.md +108 -0
- package/dist/postmark/docs/transactional-email/DOC.md +1168 -0
- package/dist/prisma/docs/orm/javascript/DOC.md +1419 -0
- package/dist/prisma/docs/orm/python/DOC.md +1317 -0
- package/dist/qdrant/docs/vector-search/javascript/DOC.md +1221 -0
- package/dist/qdrant/docs/vector-search/python/DOC.md +1653 -0
- package/dist/rabbitmq/docs/message-queue/javascript/DOC.md +1193 -0
- package/dist/rabbitmq/docs/message-queue/python/DOC.md +1243 -0
- package/dist/razorpay/docs/payments/javascript/DOC.md +1219 -0
- package/dist/razorpay/docs/payments/python/DOC.md +1330 -0
- package/dist/redis/docs/key-value/javascript/DOC.md +1851 -0
- package/dist/redis/docs/key-value/python/DOC.md +2054 -0
- package/dist/registry.json +2817 -0
- package/dist/replicate/docs/model-hosting/DOC.md +1318 -0
- package/dist/resend/docs/email/DOC.md +1271 -0
- package/dist/salesforce/docs/crm/javascript/DOC.md +1241 -0
- package/dist/salesforce/docs/crm/python/DOC.md +1183 -0
- package/dist/search-index.json +1 -0
- package/dist/sendgrid/docs/email-api/javascript/DOC.md +371 -0
- package/dist/sendgrid/docs/email-api/python/DOC.md +656 -0
- package/dist/sentry/docs/error-tracking/javascript/DOC.md +1073 -0
- package/dist/sentry/docs/error-tracking/python/DOC.md +1309 -0
- package/dist/shopify/docs/storefront/DOC.md +457 -0
- package/dist/slack/docs/workspace/javascript/DOC.md +933 -0
- package/dist/slack/docs/workspace/python/DOC.md +271 -0
- package/dist/square/docs/payments/javascript/DOC.md +1855 -0
- package/dist/square/docs/payments/python/DOC.md +1728 -0
- package/dist/stripe/docs/api/DOC.md +1727 -0
- package/dist/stripe/docs/payments/DOC.md +1726 -0
- package/dist/stytch/docs/auth/javascript/DOC.md +1813 -0
- package/dist/stytch/docs/auth/python/DOC.md +1962 -0
- package/dist/supabase/docs/client/DOC.md +1606 -0
- package/dist/twilio/docs/messaging/python/DOC.md +469 -0
- package/dist/twilio/docs/messaging/typescript/DOC.md +946 -0
- package/dist/vercel/docs/platform/DOC.md +1940 -0
- package/dist/weaviate/docs/vector-db/javascript/DOC.md +1268 -0
- package/dist/weaviate/docs/vector-db/python/DOC.md +1388 -0
- package/dist/zendesk/docs/support/javascript/DOC.md +2150 -0
- package/dist/zendesk/docs/support/python/DOC.md +2297 -0
- package/package.json +22 -6
- package/skills/get-api-docs/SKILL.md +84 -0
- package/src/commands/annotate.js +83 -0
- package/src/commands/build.js +12 -1
- package/src/commands/feedback.js +150 -0
- package/src/commands/get.js +83 -42
- package/src/commands/search.js +7 -0
- package/src/index.js +43 -17
- package/src/lib/analytics.js +90 -0
- package/src/lib/annotations.js +57 -0
- package/src/lib/bm25.js +170 -0
- package/src/lib/cache.js +69 -6
- package/src/lib/config.js +8 -3
- package/src/lib/identity.js +99 -0
- package/src/lib/registry.js +103 -20
- package/src/lib/telemetry.js +86 -0
- package/src/mcp/server.js +177 -0
- package/src/mcp/tools.js +251 -0
|
@@ -0,0 +1,656 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: email-api
|
|
3
|
+
description: "SendGrid Python SDK for sending emails and managing email communications via the SendGrid API."
|
|
4
|
+
metadata:
|
|
5
|
+
languages: "python"
|
|
6
|
+
versions: "6.12.5"
|
|
7
|
+
updated-on: "2026-03-01"
|
|
8
|
+
source: maintainer
|
|
9
|
+
tags: "sendgrid,email,transactional,templates,delivery"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# SendGrid API Coding Guidelines (Python)
|
|
13
|
+
|
|
14
|
+
You are a SendGrid API coding expert. Help me with writing code using the SendGrid Python SDK for sending emails and managing email communications.
|
|
15
|
+
|
|
16
|
+
Please follow the following guidelines when generating code.
|
|
17
|
+
|
|
18
|
+
You can find the official SDK documentation and code samples here:
|
|
19
|
+
https://sendgrid.com/docs/
|
|
20
|
+
|
|
21
|
+
## Golden Rule: Use the Correct and Current SDK
|
|
22
|
+
|
|
23
|
+
Always use the official SendGrid Python SDK to send emails and interact with SendGrid's Web API v3.
|
|
24
|
+
|
|
25
|
+
- **Library Name:** SendGrid Python SDK
|
|
26
|
+
- **Python Package:** `sendgrid`
|
|
27
|
+
- **GitHub Repository:** sendgrid/sendgrid-python
|
|
28
|
+
|
|
29
|
+
**Installation:**
|
|
30
|
+
|
|
31
|
+
- **Correct:** `pip install sendgrid`
|
|
32
|
+
|
|
33
|
+
**APIs and Usage:**
|
|
34
|
+
|
|
35
|
+
- **Correct:** `from sendgrid import SendGridAPIClient`
|
|
36
|
+
- **Correct:** `from sendgrid.helpers.mail import Mail`
|
|
37
|
+
- **Correct:** `sg = SendGridAPIClient(api_key=os.environ.get('SENDGRID_API_KEY'))`
|
|
38
|
+
|
|
39
|
+
## Initialization and API Key
|
|
40
|
+
|
|
41
|
+
The SendGrid Python library requires creating a client object for all API calls.
|
|
42
|
+
|
|
43
|
+
- Always use `SendGridAPIClient()` to create a client object.
|
|
44
|
+
- Set `SENDGRID_API_KEY` environment variable, which will be picked up automatically.
|
|
45
|
+
- API key can also be passed directly to the constructor.
|
|
46
|
+
|
|
47
|
+
## Environment Setup
|
|
48
|
+
|
|
49
|
+
### Setting API Key Environment Variable
|
|
50
|
+
|
|
51
|
+
**Mac/Linux:**
|
|
52
|
+
```bash
|
|
53
|
+
export SENDGRID_API_KEY='YOUR_API_KEY'
|
|
54
|
+
# Or create a .env file
|
|
55
|
+
echo "SENDGRID_API_KEY='YOUR_API_KEY'" > .env
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**Windows:**
|
|
59
|
+
```bash
|
|
60
|
+
set SENDGRID_API_KEY=YOUR_API_KEY
|
|
61
|
+
# Or permanently:
|
|
62
|
+
setx SENDGRID_API_KEY "YOUR_API_KEY"
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Basic Email Sending
|
|
66
|
+
|
|
67
|
+
### Simple Email with Mail Helper Class
|
|
68
|
+
|
|
69
|
+
Here's how to send a basic email using the Mail helper class:
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
import os
|
|
73
|
+
from sendgrid import SendGridAPIClient
|
|
74
|
+
from sendgrid.helpers.mail import Mail
|
|
75
|
+
|
|
76
|
+
# Create the email message
|
|
77
|
+
message = Mail(
|
|
78
|
+
from_email='from@example.com',
|
|
79
|
+
to_emails='to@example.com',
|
|
80
|
+
subject='Sending with SendGrid is Fun',
|
|
81
|
+
html_content='<strong>and easy to do anywhere, even with Python</strong>'
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
# Send the email
|
|
85
|
+
try:
|
|
86
|
+
sg = SendGridAPIClient(os.environ.get('SENDGRID_API_KEY'))
|
|
87
|
+
response = sg.send(message)
|
|
88
|
+
print(response.status_code)
|
|
89
|
+
print(response.body)
|
|
90
|
+
print(response.headers)
|
|
91
|
+
except Exception as e:
|
|
92
|
+
print(e)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Email with Both HTML and Plain Text Content
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
from sendgrid.helpers.mail import Mail, Content
|
|
99
|
+
from sendgrid import SendGridAPIClient
|
|
100
|
+
import os
|
|
101
|
+
|
|
102
|
+
message = Mail(
|
|
103
|
+
from_email='from@example.com',
|
|
104
|
+
to_emails='to@example.com',
|
|
105
|
+
subject='Multi-format Email'
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
# Add plain text content
|
|
109
|
+
message.add_content(Content("text/plain", "Hello, this is plain text!"))
|
|
110
|
+
|
|
111
|
+
# Add HTML content
|
|
112
|
+
message.add_content(Content("text/html", "<strong>Hello, this is HTML!</strong>"))
|
|
113
|
+
|
|
114
|
+
sg = SendGridAPIClient(os.environ.get('SENDGRID_API_KEY'))
|
|
115
|
+
response = sg.send(message)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Advanced Email Features
|
|
119
|
+
|
|
120
|
+
### Using Dynamic Templates
|
|
121
|
+
|
|
122
|
+
For emails with dynamic content using SendGrid templates:
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
import os
|
|
126
|
+
from sendgrid import SendGridAPIClient
|
|
127
|
+
from sendgrid.helpers.mail import Mail
|
|
128
|
+
|
|
129
|
+
message = Mail(
|
|
130
|
+
from_email='from@example.com',
|
|
131
|
+
to_emails='to@example.com'
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
# Set template ID and dynamic data
|
|
135
|
+
message.template_id = 'd-your-template-id-here'
|
|
136
|
+
message.dynamic_template_data = {
|
|
137
|
+
'subject': 'Testing Templates',
|
|
138
|
+
'name': 'John Doe',
|
|
139
|
+
'city': 'San Francisco'
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
sg = SendGridAPIClient(os.environ.get('SENDGRID_API_KEY'))
|
|
143
|
+
response = sg.send(message)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Multiple Recipients
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
from sendgrid.helpers.mail import Mail, To
|
|
150
|
+
|
|
151
|
+
# Multiple recipients in to_emails list
|
|
152
|
+
message = Mail(
|
|
153
|
+
from_email='from@example.com',
|
|
154
|
+
to_emails=['user1@example.com', 'user2@example.com', 'user3@example.com'],
|
|
155
|
+
subject='Email to Multiple Recipients',
|
|
156
|
+
html_content='<strong>Hello everyone!</strong>'
|
|
157
|
+
)
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Adding Attachments
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
from sendgrid.helpers.mail import Mail, Attachment
|
|
164
|
+
import base64
|
|
165
|
+
|
|
166
|
+
message = Mail(
|
|
167
|
+
from_email='from@example.com',
|
|
168
|
+
to_emails='to@example.com',
|
|
169
|
+
subject='Email with Attachment',
|
|
170
|
+
html_content='<strong>Please see attached file</strong>'
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
# Add attachment
|
|
174
|
+
with open('path/to/file.pdf', 'rb') as f:
|
|
175
|
+
data = f.read()
|
|
176
|
+
f.close()
|
|
177
|
+
|
|
178
|
+
encoded_file = base64.b64encode(data).decode()
|
|
179
|
+
attachment = Attachment(
|
|
180
|
+
file_content=encoded_file,
|
|
181
|
+
file_name='attachment.pdf',
|
|
182
|
+
file_type='application/pdf',
|
|
183
|
+
disposition='attachment'
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
message.add_attachment(attachment)
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Personalizations for Individual Recipients
|
|
190
|
+
|
|
191
|
+
```python
|
|
192
|
+
from sendgrid.helpers.mail import Mail, Personalization, To, Subject
|
|
193
|
+
|
|
194
|
+
message = Mail()
|
|
195
|
+
message.from_email = 'from@example.com'
|
|
196
|
+
|
|
197
|
+
# Create separate personalization for each recipient
|
|
198
|
+
personalization1 = Personalization()
|
|
199
|
+
personalization1.add_to(To('user1@example.com', 'User One'))
|
|
200
|
+
personalization1.subject = 'Personal message for User One'
|
|
201
|
+
|
|
202
|
+
personalization2 = Personalization()
|
|
203
|
+
personalization2.add_to(To('user2@example.com', 'User Two'))
|
|
204
|
+
personalization2.subject = 'Personal message for User Two'
|
|
205
|
+
|
|
206
|
+
message.add_personalization(personalization1)
|
|
207
|
+
message.add_personalization(personalization2)
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## API Client Usage
|
|
211
|
+
|
|
212
|
+
### General Web API Usage (Fluent Interface)
|
|
213
|
+
|
|
214
|
+
For accessing other SendGrid APIs beyond mail sending:
|
|
215
|
+
|
|
216
|
+
```python
|
|
217
|
+
import os
|
|
218
|
+
from sendgrid import SendGridAPIClient
|
|
219
|
+
|
|
220
|
+
sg = SendGridAPIClient(os.environ.get('SENDGRID_API_KEY'))
|
|
221
|
+
|
|
222
|
+
# Get bounce suppressions
|
|
223
|
+
response = sg.client.suppression.bounces.get()
|
|
224
|
+
print(response.status_code)
|
|
225
|
+
print(response.body)
|
|
226
|
+
|
|
227
|
+
# Get API keys
|
|
228
|
+
response = sg.client.api_keys.get()
|
|
229
|
+
print(response.status_code)
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Alternative API Usage (Non-Fluent Interface)
|
|
233
|
+
|
|
234
|
+
```python
|
|
235
|
+
import os
|
|
236
|
+
from sendgrid import SendGridAPIClient
|
|
237
|
+
|
|
238
|
+
sg = SendGridAPIClient(os.environ.get('SENDGRID_API_KEY'))
|
|
239
|
+
|
|
240
|
+
# Using string path instead of fluent interface
|
|
241
|
+
response = sg.client._("suppression/bounces").get()
|
|
242
|
+
print(response.status_code)
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Configuration Options
|
|
246
|
+
|
|
247
|
+
### Data Residency
|
|
248
|
+
|
|
249
|
+
For EU data residency compliance:
|
|
250
|
+
|
|
251
|
+
```python
|
|
252
|
+
from sendgrid import SendGridAPIClient
|
|
253
|
+
|
|
254
|
+
sg = SendGridAPIClient(api_key='your-api-key')
|
|
255
|
+
sg.set_sendgrid_data_residency('eu') # 'eu' or 'global'
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Subuser Impersonation
|
|
259
|
+
|
|
260
|
+
To send emails on behalf of a subuser:
|
|
261
|
+
|
|
262
|
+
```python
|
|
263
|
+
from sendgrid import SendGridAPIClient
|
|
264
|
+
|
|
265
|
+
sg = SendGridAPIClient(
|
|
266
|
+
api_key='your-api-key',
|
|
267
|
+
impersonate_subuser='subuser@example.com'
|
|
268
|
+
)
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## Error Handling
|
|
272
|
+
|
|
273
|
+
Always wrap SendGrid API calls in try-catch blocks:
|
|
274
|
+
|
|
275
|
+
```python
|
|
276
|
+
import os
|
|
277
|
+
from sendgrid import SendGridAPIClient
|
|
278
|
+
from sendgrid.helpers.mail import Mail
|
|
279
|
+
|
|
280
|
+
message = Mail(
|
|
281
|
+
from_email='from@example.com',
|
|
282
|
+
to_emails='to@example.com',
|
|
283
|
+
subject='Test Email',
|
|
284
|
+
html_content='<strong>Test</strong>'
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
try:
|
|
288
|
+
sg = SendGridAPIClient(os.environ.get('SENDGRID_API_KEY'))
|
|
289
|
+
response = sg.send(message)
|
|
290
|
+
|
|
291
|
+
# Check response status
|
|
292
|
+
if response.status_code >= 200 and response.status_code < 300:
|
|
293
|
+
print("Email sent successfully!")
|
|
294
|
+
else:
|
|
295
|
+
print(f"Failed to send email. Status: {response.status_code}")
|
|
296
|
+
print(f"Response: {response.body}")
|
|
297
|
+
|
|
298
|
+
except Exception as e:
|
|
299
|
+
print(f"Error occurred: {e}")
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
## Best Practices
|
|
303
|
+
|
|
304
|
+
### Mail Helper Class vs Raw JSON
|
|
305
|
+
|
|
306
|
+
- **Recommended:** Use the Mail helper class for building emails as it provides validation and easier syntax
|
|
307
|
+
- **Alternative:** Raw JSON can be used but requires manual construction of the payload
|
|
308
|
+
|
|
309
|
+
### Content Ordering
|
|
310
|
+
|
|
311
|
+
When adding multiple content types, follow this order:
|
|
312
|
+
1. Plain text (`text/plain`)
|
|
313
|
+
2. AMP HTML (`text/x-amp-html`)
|
|
314
|
+
3. HTML (`text/html`)
|
|
315
|
+
|
|
316
|
+
### Environment Variables
|
|
317
|
+
|
|
318
|
+
- Always use environment variables for API keys, never hardcode them
|
|
319
|
+
- Use `.env` files for local development and add them to `.gitignore`
|
|
320
|
+
|
|
321
|
+
## Common Mail Helper Classes
|
|
322
|
+
|
|
323
|
+
Key classes from `sendgrid.helpers.mail`:
|
|
324
|
+
|
|
325
|
+
- `Mail` - Main email builder class
|
|
326
|
+
- `From` - Sender email address
|
|
327
|
+
- `To`, `Cc`, `Bcc` - Recipient email addresses
|
|
328
|
+
- `Content` - Email content (HTML/plain text)
|
|
329
|
+
- `Attachment` - File attachments
|
|
330
|
+
- `Personalization` - Individual recipient customization
|
|
331
|
+
- `TemplateId` - Dynamic template reference
|
|
332
|
+
|
|
333
|
+
## Useful Links
|
|
334
|
+
|
|
335
|
+
- **Documentation:** https://sendgrid.com/docs/
|
|
336
|
+
- **API Reference:** https://sendgrid.com/docs/API_Reference/index.html
|
|
337
|
+
- **Library Documentation:** https://github.com/sendgrid/sendgrid-python
|
|
338
|
+
- **Use Cases:** https://github.com/sendgrid/sendgrid-python/tree/main/use_cases
|
|
339
|
+
- **Troubleshooting:** https://github.com/sendgrid/sendgrid-python/blob/main/TROUBLESHOOTING.md
|
|
340
|
+
|
|
341
|
+
## Notes
|
|
342
|
+
|
|
343
|
+
This SendGrid Python SDK provides full support for SendGrid Web API v3 endpoints. The library uses the Mail helper class as the primary interface for building and sending emails, with the SendGridAPIClient handling the actual API communication. Always ensure proper error handling and use environment variables for sensitive data like API keys.
|
|
344
|
+
|
|
345
|
+
## Official Documentation
|
|
346
|
+
|
|
347
|
+
**This library allows you to quickly and easily use the SendGrid Web API v3 via Python.**
|
|
348
|
+
|
|
349
|
+
Version 3.X.X+ of this library provides full support for all SendGrid [Web API v3](https://sendgrid.com/docs/API_Reference/Web_API_v3/index.html) endpoints, including the new [v3 /mail/send](https://sendgrid.com/blog/introducing-v3mailsend-sendgrids-new-mail-endpoint).
|
|
350
|
+
|
|
351
|
+
### Alternative Environment Setup Methods
|
|
352
|
+
|
|
353
|
+
#### Mac
|
|
354
|
+
|
|
355
|
+
Update the development environment with your [SENDGRID_API_KEY](https://app.sendgrid.com/settings/api_keys) (more info [here](https://sendgrid.com/docs/User_Guide/Settings/api_keys.html)), for example:
|
|
356
|
+
|
|
357
|
+
```bash
|
|
358
|
+
echo "export SENDGRID_API_KEY='YOUR_API_KEY'" > sendgrid.env
|
|
359
|
+
echo "sendgrid.env" >> .gitignore
|
|
360
|
+
source ./sendgrid.env
|
|
361
|
+
```
|
|
362
|
+
SendGrid also supports local environment file `.env`. Copy or rename `.env_sample` into `.env` and update [SENDGRID_API_KEY](https://app.sendgrid.com/settings/api_keys) with your key.
|
|
363
|
+
|
|
364
|
+
#### Windows
|
|
365
|
+
Temporarily set the environment variable(accessible only during the current cli session):
|
|
366
|
+
```bash
|
|
367
|
+
set SENDGRID_API_KEY=YOUR_API_KEY
|
|
368
|
+
```
|
|
369
|
+
Permanently set the environment variable(accessible in all subsequent cli sessions):
|
|
370
|
+
```bash
|
|
371
|
+
setx SENDGRID_API_KEY "YOUR_API_KEY"
|
|
372
|
+
```
|
|
373
|
+
## Code Examples
|
|
374
|
+
|
|
375
|
+
### With Mail Helper Class
|
|
376
|
+
|
|
377
|
+
```python
|
|
378
|
+
import sendgrid
|
|
379
|
+
import os
|
|
380
|
+
from sendgrid.helpers.mail import *
|
|
381
|
+
|
|
382
|
+
sg = sendgrid.SendGridAPIClient(api_key=os.environ.get('SENDGRID_API_KEY'))
|
|
383
|
+
from_email = Email("test@example.com")
|
|
384
|
+
to_email = To("test@example.com")
|
|
385
|
+
subject = "Sending with SendGrid is Fun"
|
|
386
|
+
content = Content("text/plain", "and easy to do anywhere, even with Python")
|
|
387
|
+
mail = Mail(from_email, to_email, subject, content)
|
|
388
|
+
response = sg.client.mail.send.post(request_body=mail.get())
|
|
389
|
+
print(response.status_code)
|
|
390
|
+
print(response.body)
|
|
391
|
+
print(response.headers)
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
### Fluent API Usage
|
|
395
|
+
|
|
396
|
+
```python
|
|
397
|
+
import sendgrid
|
|
398
|
+
import os
|
|
399
|
+
|
|
400
|
+
sg = sendgrid.SendGridAPIClient(api_key=os.environ.get('SENDGRID_API_KEY'))
|
|
401
|
+
response = sg.client.suppression.bounces.get()
|
|
402
|
+
print(response.status_code)
|
|
403
|
+
print(response.body)
|
|
404
|
+
print(response.headers)
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
### Non-Fluent API Usage
|
|
408
|
+
|
|
409
|
+
```python
|
|
410
|
+
import sendgrid
|
|
411
|
+
import os
|
|
412
|
+
|
|
413
|
+
sg = sendgrid.SendGridAPIClient(api_key=os.environ.get('SENDGRID_API_KEY'))
|
|
414
|
+
response = sg.client._("suppression/bounces").get()
|
|
415
|
+
print(response.status_code)
|
|
416
|
+
print(response.body)
|
|
417
|
+
print(response.headers)
|
|
418
|
+
```
|
|
419
|
+
## SendGridAPIClient Constructor
|
|
420
|
+
|
|
421
|
+
```python
|
|
422
|
+
def __init__(
|
|
423
|
+
self,
|
|
424
|
+
api_key=None,
|
|
425
|
+
host='https://api.sendgrid.com',
|
|
426
|
+
impersonate_subuser=None):
|
|
427
|
+
"""
|
|
428
|
+
Construct the Twilio SendGrid v3 API object.
|
|
429
|
+
Note that the underlying client is being set up during initialization,
|
|
430
|
+
therefore changing attributes in runtime will not affect HTTP client
|
|
431
|
+
behaviour.
|
|
432
|
+
|
|
433
|
+
:param api_key: Twilio SendGrid API key to use. If not provided, value
|
|
434
|
+
will be read from environment variable "SENDGRID_API_KEY"
|
|
435
|
+
:type api_key: string
|
|
436
|
+
:param impersonate_subuser: the subuser to impersonate. Will be passed
|
|
437
|
+
by "On-Behalf-Of" header by underlying
|
|
438
|
+
client. See
|
|
439
|
+
https://sendgrid.com/docs/User_Guide/Settings/subusers.html
|
|
440
|
+
for more details
|
|
441
|
+
:type impersonate_subuser: string
|
|
442
|
+
:param host: base URL for API calls
|
|
443
|
+
:type host: string
|
|
444
|
+
"""
|
|
445
|
+
self.api_key = api_key or os.environ.get('SENDGRID_API_KEY')
|
|
446
|
+
auth = 'Bearer {}'.format(self.api_key)
|
|
447
|
+
|
|
448
|
+
super(SendGridAPIClient, self).__init__(auth, host, impersonate_subuser)
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
## Helper Class Imports
|
|
452
|
+
|
|
453
|
+
```python
|
|
454
|
+
from .bcc_email import Bcc
|
|
455
|
+
from .cc_email import Cc
|
|
456
|
+
from .content import Content
|
|
457
|
+
from .custom_arg import CustomArg
|
|
458
|
+
from .dynamic_template_data import DynamicTemplateData
|
|
459
|
+
from .email import Email
|
|
460
|
+
from .from_email import From
|
|
461
|
+
from .header import Header
|
|
462
|
+
from .mime_type import MimeType
|
|
463
|
+
from .personalization import Personalization
|
|
464
|
+
from .reply_to import ReplyTo
|
|
465
|
+
from .send_at import SendAt
|
|
466
|
+
from .subject import Subject
|
|
467
|
+
from .substitution import Substitution
|
|
468
|
+
from .template_id import TemplateId
|
|
469
|
+
from .to_email import To
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
## Mail Methods
|
|
473
|
+
|
|
474
|
+
### Add Personalization
|
|
475
|
+
|
|
476
|
+
```python
|
|
477
|
+
def add_personalization(self, personalization, index=0):
|
|
478
|
+
"""Add a Personalization object
|
|
479
|
+
|
|
480
|
+
:param personalization: Add a Personalization object
|
|
481
|
+
:type personalization: Personalization
|
|
482
|
+
:param index: The index where to add the Personalization
|
|
483
|
+
:type index: int
|
|
484
|
+
"""
|
|
485
|
+
self._personalizations = self._ensure_append(
|
|
486
|
+
personalization, self._personalizations, index)
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
### Add To Recipients
|
|
490
|
+
|
|
491
|
+
```python
|
|
492
|
+
def add_to(
|
|
493
|
+
self, to_email, global_substitutions=None, is_multiple=False, p=0):
|
|
494
|
+
"""Adds a To object to the Personalization object
|
|
495
|
+
|
|
496
|
+
:param to_email: A To object
|
|
497
|
+
:type to_email: To, str, tuple, list(str), list(tuple), list(To)
|
|
498
|
+
:param global_substitutions: A dict of substitutions for all recipients
|
|
499
|
+
:type global_substitutions: dict
|
|
500
|
+
:param is_multiple: Create a new personalization for each recipient
|
|
501
|
+
:type is_multiple: bool
|
|
502
|
+
:param p: p is the Personalization object or Personalization object
|
|
503
|
+
index
|
|
504
|
+
:type p: Personalization, integer, optional
|
|
505
|
+
"""
|
|
506
|
+
|
|
507
|
+
if isinstance(to_email, list):
|
|
508
|
+
for email in to_email:
|
|
509
|
+
if isinstance(email, str):
|
|
510
|
+
email = To(email, None)
|
|
511
|
+
elif isinstance(email, tuple):
|
|
512
|
+
email = To(email[0], email[1])
|
|
513
|
+
elif not isinstance(email, Email):
|
|
514
|
+
raise ValueError(
|
|
515
|
+
'Please use a To/Cc/Bcc, tuple, or a str for a to_email list.'
|
|
516
|
+
)
|
|
517
|
+
self._set_emails(email, global_substitutions, is_multiple, p)
|
|
518
|
+
else:
|
|
519
|
+
if isinstance(to_email, str):
|
|
520
|
+
to_email = To(to_email, None)
|
|
521
|
+
if isinstance(to_email, tuple):
|
|
522
|
+
to_email = To(to_email[0], to_email[1])
|
|
523
|
+
if isinstance(to_email, Email):
|
|
524
|
+
p = to_email.personalization
|
|
525
|
+
self._set_emails(to_email, global_substitutions, is_multiple, p)
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
### Add Content
|
|
529
|
+
|
|
530
|
+
```python
|
|
531
|
+
def add_content(self, content, mime_type=None):
|
|
532
|
+
"""Add content to the email
|
|
533
|
+
|
|
534
|
+
:param contents: Content to be added to the email
|
|
535
|
+
:type contents: Content
|
|
536
|
+
:param mime_type: Override the mime type
|
|
537
|
+
:type mime_type: MimeType, str
|
|
538
|
+
"""
|
|
539
|
+
if isinstance(content, str):
|
|
540
|
+
content = Content(mime_type, content)
|
|
541
|
+
# Content of mime type text/plain must always come first, followed by text/x-amp-html and then text/html
|
|
542
|
+
if content.mime_type == MimeType.text:
|
|
543
|
+
self._contents = self._ensure_insert(content, self._contents)
|
|
544
|
+
elif content.mime_type == MimeType.amp:
|
|
545
|
+
if self._contents:
|
|
546
|
+
for _content in self._contents:
|
|
547
|
+
# this is written in the context that plain text content will always come earlier than the html content
|
|
548
|
+
if _content.mime_type == MimeType.text:
|
|
549
|
+
index = 1
|
|
550
|
+
break
|
|
551
|
+
elif _content.mime_type == MimeType.html:
|
|
552
|
+
index = 0
|
|
553
|
+
break
|
|
554
|
+
else:
|
|
555
|
+
index = 0
|
|
556
|
+
self._contents = self._ensure_append(
|
|
557
|
+
content, self._contents, index=index)
|
|
558
|
+
else:
|
|
559
|
+
if self._contents:
|
|
560
|
+
index = len(self._contents)
|
|
561
|
+
else:
|
|
562
|
+
index = 0
|
|
563
|
+
self._contents = self._ensure_append(
|
|
564
|
+
content, self._contents, index=index)
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
### Add Attachment
|
|
568
|
+
|
|
569
|
+
```python
|
|
570
|
+
def add_attachment(self, attachment):
|
|
571
|
+
"""Add an attachment to this email
|
|
572
|
+
|
|
573
|
+
:param attachment: Add an attachment to this email
|
|
574
|
+
:type attachment: Attachment
|
|
575
|
+
"""
|
|
576
|
+
self._attachments = self._ensure_append(attachment, self._attachments)
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
## Template Usage Example
|
|
580
|
+
|
|
581
|
+
```python
|
|
582
|
+
message = Mail(
|
|
583
|
+
from_email='from_email@example.com',
|
|
584
|
+
to_emails='to@example.com',
|
|
585
|
+
html_content='<strong>and easy to do anywhere, even with Python</strong>')
|
|
586
|
+
message.dynamic_template_data = {
|
|
587
|
+
'subject': 'Testing Templates',
|
|
588
|
+
'name': 'Some One',
|
|
589
|
+
'city': 'Denver'
|
|
590
|
+
}
|
|
591
|
+
message.template_id = 'd-f43daeeaef504760851f727007e0b5d0'
|
|
592
|
+
try:
|
|
593
|
+
sendgrid_client = SendGridAPIClient(os.environ.get('SENDGRID_API_KEY'))
|
|
594
|
+
response = sendgrid_client.send(message)
|
|
595
|
+
print(response.status_code)
|
|
596
|
+
print(response.body)
|
|
597
|
+
print(response.headers)
|
|
598
|
+
except Exception as e:
|
|
599
|
+
print(e.message)
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
## SendGridAPIClient Methods
|
|
603
|
+
|
|
604
|
+
### Send Method
|
|
605
|
+
|
|
606
|
+
```python
|
|
607
|
+
def send(self, message):
|
|
608
|
+
"""Make a Twilio SendGrid v3 API request with the request body generated by
|
|
609
|
+
the Mail object
|
|
610
|
+
|
|
611
|
+
:param message: The Twilio SendGrid v3 API request body generated by the Mail
|
|
612
|
+
object
|
|
613
|
+
:type message: Mail
|
|
614
|
+
"""
|
|
615
|
+
if not isinstance(message, dict):
|
|
616
|
+
message = message.get()
|
|
617
|
+
|
|
618
|
+
return self.client.mail.send.post(request_body=message)
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
### Data Residency Method
|
|
622
|
+
|
|
623
|
+
```python
|
|
624
|
+
def set_sendgrid_data_residency(self, region):
|
|
625
|
+
"""
|
|
626
|
+
Client libraries contain setters for specifying region/edge.
|
|
627
|
+
This supports global and eu regions only. This set will likely expand in the future.
|
|
628
|
+
Global is the default residency (or region)
|
|
629
|
+
Global region means the message will be sent through https://api.sendgrid.com
|
|
630
|
+
EU region means the message will be sent through https://api.eu.sendgrid.com
|
|
631
|
+
:param region: string
|
|
632
|
+
:return:
|
|
633
|
+
"""
|
|
634
|
+
if region in region_host_dict.keys():
|
|
635
|
+
self.host = region_host_dict[region]
|
|
636
|
+
if self._default_headers is not None:
|
|
637
|
+
self.client = python_http_client.Client(
|
|
638
|
+
host=self.host,
|
|
639
|
+
request_headers=self._default_headers,
|
|
640
|
+
version=3)
|
|
641
|
+
else:
|
|
642
|
+
raise ValueError("region can only be \"eu\" or \"global\"")
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
## Final Error Handling Example
|
|
646
|
+
|
|
647
|
+
```python
|
|
648
|
+
try:
|
|
649
|
+
sendgrid_client = SendGridAPIClient(os.environ.get('SENDGRID_API_KEY'))
|
|
650
|
+
response = sendgrid_client.send(message)
|
|
651
|
+
print(response.status_code)
|
|
652
|
+
print(response.body)
|
|
653
|
+
print(response.headers)
|
|
654
|
+
except Exception as e:
|
|
655
|
+
print(e)
|
|
656
|
+
```
|