Polly v8 resilience for SendGrid — add retry, timeout, and circuit-breaker to any email send in two lines.
var client = new SendGridClient(apiKey);
var resilient = client.WithPolly(pipeline => pipeline
.AddRetry(new RetryStrategyOptions
{
MaxRetryAttempts = 3,
Delay = TimeSpan.FromSeconds(2),
BackoffType = DelayBackoffType.Exponential,
UseJitter = true,
ShouldHandle = SendGridTransientErrors.IsTransient,
})
.AddTimeout(TimeSpan.FromSeconds(30)));
var response = await resilient.SendEmailAsync(msg);SendGrid imposes rate limits and occasionally returns transient errors. Dropped emails mean missed notifications, failed password resets, and lost revenue. This library wraps the response check in Polly v8 so every transient error is retried automatically:
| Problem | Solution |
|---|---|
| HTTP 429 rate limit exceeded | Auto-thrown as SendGridTransientException and retried |
| HTTP 500 transient server error | Auto-thrown as SendGridTransientException and retried |
| HTTP 503 service unavailable | Auto-thrown as SendGridTransientException and retried |
HttpRequestException network failure |
Caught by SendGridTransientErrors.IsTransient |
| Cascading failures during an outage | Wrap with AddCircuitBreaker |
dotnet add package PollySendGrid
dotnet add package Polly.Core
var client = new SendGridClient(apiKey);
var resilient = client.WithPolly(p => p
.AddRetry(new RetryStrategyOptions
{
MaxRetryAttempts = 3,
Delay = TimeSpan.FromSeconds(2),
BackoffType = DelayBackoffType.Exponential,
UseJitter = true,
ShouldHandle = SendGridTransientErrors.IsTransient,
}));
var msg = MailHelper.CreateSingleEmail(from, to, subject, plainText, htmlContent);
var response = await resilient.SendEmailAsync(msg);builder.Services.AddPollySendGrid(apiKey, pipeline => pipeline
.AddRetry(new RetryStrategyOptions
{
MaxRetryAttempts = 3,
Delay = TimeSpan.FromSeconds(2),
BackoffType = DelayBackoffType.Exponential,
UseJitter = true,
ShouldHandle = SendGridTransientErrors.IsTransient,
})
.AddTimeout(TimeSpan.FromSeconds(30)));
public class EmailService(ResilientSendGridClient client)
{
public Task<Response> SendAsync(SendGridMessage msg, CancellationToken ct)
=> client.SendEmailAsync(msg, ct);
}| Condition | Why it's transient |
|---|---|
SendGridTransientException (HTTP 429) |
Rate limit — back off and retry |
SendGridTransientException (HTTP 500) |
Transient server error |
SendGridTransientException (HTTP 503) |
Service maintenance or overload |
HttpRequestException |
Network failure |
Note:
SendGridTransientExceptionis thrown automatically byResilientSendGridClientwhen the response status code is inSendGridTransientErrors.StatusCodes. You do not throw it yourself.
| Member | Description |
|---|---|
ResilientSendGridClient.Inner |
The underlying ISendGridClient |
SendEmailAsync(msg, ct) |
Sends an email through the pipeline |
ExecuteAsync<T>(operation, ct) |
Runs any ISendGridClient operation through the pipeline |
SendGridTransientErrors.IsTransient |
PredicateBuilder for 429/500/503 + HttpRequestException |
SendGridTransientErrors.StatusCodes |
IReadOnlySet<HttpStatusCode> — TooManyRequests, InternalServerError, ServiceUnavailable |
client.WithPolly(pipeline) |
Wraps ISendGridClient with a pre-built pipeline |
client.WithPolly(configure) |
Builds pipeline inline and wraps the client |
services.AddPollySendGrid(configure) |
DI registration (requires ISendGridClient in DI) |
services.AddPollySendGrid(apiKey, configure) |
DI registration with API key shortcut |
.NET 6 ✅ · .NET 8 ✅ · .NET 9 ✅
| Package | Description |
|---|---|
| PollyAzureBlob | Polly v8 for Azure Blob Storage |
| PollyAzureServiceBus | Polly v8 for Azure Service Bus |
| PollyAzureKeyVault | Polly v8 for Azure Key Vault |
| PollyAzureEventHub | Polly v8 for Azure Event Hubs |
| PollyCosmosDb | Polly v8 for Azure Cosmos DB |
| PollyElasticsearch | Polly v8 for Elasticsearch |
| PollyRedis | Polly v8 for StackExchange.Redis |
| PollyEFCore | Polly v8 for Entity Framework Core |
| PollyDapper | Polly v8 for Dapper |
| PollyMongo | Polly v8 for MongoDB |
| PollyNpgsql | Polly v8 for Npgsql (PostgreSQL) |
| PollySqlClient | Polly v8 for Microsoft.Data.SqlClient |
| PollyGrpc | Polly v8 for gRPC |
| PollyRabbitMQ | Polly v8 for RabbitMQ |
| PollyKafka | Polly v8 for Confluent.Kafka |
| PollySignalR | Polly v8 for SignalR |
| PollyOpenAI | Polly v8 for OpenAI .NET SDK |
| PollyMediatR | Polly v8 for MediatR |
| PollyHealthChecks | Polly v8 for ASP.NET Core Health Checks |
| PollyMassTransit | Polly v8 for MassTransit |
| PollyAzureTableStorage | Polly v8 for Azure Table Storage |
| PollyMailKit | MailKit SMTP email client |
| PollyAzureQueueStorage | Azure Queue Storage QueueClient |
| PollyHangfire | Hangfire IBackgroundJobClient |
| PollyBackoff | Polly v8 backoff helpers |
The author of this package is available for consulting on Polly v8 resilience, Azure cloud architecture, and clean .NET design.
→ solidqualitysolutions.com · LinkedIn
MIT © Justin Bannister