Shib™ 🚀Originally published on API Status Check TLDR: Your SaaS application is only as reliable as the...
Originally published on API Status Check
TLDR: Your SaaS application is only as reliable as the third-party APIs it depends on. Learn how to set up automated API outage alerts, handle graceful degradation, and integrate webhooks and RSS feeds to keep your application running smoothly even when external services go down.
Modern SaaS applications don't operate in isolation. Whether you're building a fintech app, e-commerce platform, or productivity tool, you're likely depending on multiple third-party APIs for critical functionality:
Here's the problem: When these APIs go down, your application breaks—often without warning.
Real-world impact:
The cost of ignorance: According to industry reports, unplanned API downtime costs SaaS companies an average of $5,600 per minute. For high-traffic applications, that number can exceed $100,000 per hour.
The solution: Proactive API monitoring with instant alerts gives you critical minutes to respond, communicate, and implement fallbacks before your users even notice.
Not all API dependencies are equally critical. Focus on monitoring APIs that:
Pro tip: Create a dependency map of your application. List every external service, the features that depend on it, and the business impact if it goes down. This becomes your monitoring priority list.
You have two options for monitoring third-party APIs:
Option A: Use a dedicated monitoring service (recommended for most teams)
Option B: Build custom monitoring (for specific needs)
For this guide, we'll use both approaches—starting with the quick setup, then showing custom integration options.
Quick setup (< 5 minutes):
Example alert configuration:
Critical Alerts (immediate notification):
- Stripe API
- Auth0 API
- SendGrid API
High Priority (5-minute delay):
- AWS S3
- OpenAI API
- Twilio API
Digest Only (daily summary):
- GitHub API
- Slack API
Webhooks allow your application to automatically respond to API outages. When an API goes down, your webhook endpoint receives instant notification and can trigger fallback logic.
Setting up a webhook endpoint (Node.js/Express example):
// routes/webhooks.js
const express = require('express');
const router = express.Router();
const crypto = require('crypto');
// Webhook endpoint for API Status Check alerts
router.post('/api-status-webhook', async (req, res) => {
// Verify webhook signature (security best practice)
const signature = req.headers['x-webhook-signature'];
const payload = JSON.stringify(req.body);
const expectedSignature = crypto
.createHmac('sha256', process.env.WEBHOOK_SECRET)
.update(payload)
.digest('hex');
if (signature !== expectedSignature) {
return res.status(401).json({ error: 'Invalid signature' });
}
const { api, status, timestamp, message } = req.body;
// Log the alert
console.log(`[ALERT] ${api} is ${status} - ${message}`);
// Trigger automated responses based on the API
switch (api) {
case 'Stripe API':
await handleStripeOutage(status);
break;
case 'SendGrid API':
await handleEmailOutage(status);
break;
case 'Auth0 API':
await handleAuthOutage(status);
break;
}
// Send to internal alerting (Slack, PagerDuty, etc.)
await notifyTeam(api, status, message);
res.status(200).json({ received: true });
});
async function handleStripeOutage(status) {
if (status === 'down') {
// Enable maintenance mode for payment pages
await setFeatureFlag('payments_maintenance', true);
// Display user-facing banner
await createStatusBanner({
message: 'Payment processing temporarily unavailable. We\'re working on it!',
severity: 'warning'
});
// Queue payment attempts for retry
await enablePaymentRetryQueue();
} else if (status === 'up') {
// Re-enable payments
await setFeatureFlag('payments_maintenance', false);
await removeStatusBanner('payments');
await processPaymentRetryQueue();
}
}
async function handleEmailOutage(status) {
if (status === 'down') {
// Switch to backup email provider (e.g., AWS SES)
await setFeatureFlag('email_provider', 'backup');
console.log('Switched to backup email provider');
} else if (status === 'up') {
await setFeatureFlag('email_provider', 'primary');
}
}
async function handleAuthOutage(status) {
if (status === 'down') {
// Display status message on login page
await createStatusBanner({
message: 'Authentication service experiencing issues. Please try again in a few minutes.',
severity: 'error'
});
// Alert on-call engineer
await pageEngineer('auth-outage', 'critical');
} else if (status === 'up') {
await removeStatusBanner('auth');
}
}
module.exports = router;
Key benefits of webhook integration:
When an API goes down, your application shouldn't crash—it should degrade gracefully. Here's how to build resilient fallback logic:
Pattern 1: Circuit Breaker (prevent cascading failures)
// utils/circuitBreaker.js
class CircuitBreaker {
constructor(apiName, options = {}) {
this.apiName = apiName;
this.failureThreshold = options.failureThreshold || 5;
this.resetTimeout = options.resetTimeout || 60000; // 1 minute
this.failures = 0;
this.state = 'CLOSED'; // CLOSED | OPEN | HALF_OPEN
this.nextAttempt = Date.now();
}
async execute(fn) {
if (this.state === 'OPEN') {
if (Date.now() < this.nextAttempt) {
throw new Error(`Circuit breaker OPEN for ${this.apiName}`);
}
this.state = 'HALF_OPEN';
}
try {
const result = await fn();
this.onSuccess();
return result;
} catch (error) {
this.onFailure();
throw error;
}
}
onSuccess() {
this.failures = 0;
this.state = 'CLOSED';
}
onFailure() {
this.failures++;
if (this.failures >= this.failureThreshold) {
this.state = 'OPEN';
this.nextAttempt = Date.now() + this.resetTimeout;
console.log(`Circuit breaker opened for ${this.apiName}`);
}
}
}
// Usage example with Stripe
const stripeCircuit = new CircuitBreaker('Stripe', {
failureThreshold: 3,
resetTimeout: 30000
});
async function createStripeCharge(amount, customerId) {
try {
return await stripeCircuit.execute(async () => {
return await stripe.charges.create({
amount,
customer: customerId,
currency: 'usd'
});
});
} catch (error) {
// Fallback: Queue for later processing
await queuePaymentForRetry({ amount, customerId });
throw new Error('Payment processing temporarily unavailable');
}
}
Pattern 2: Retry with Exponential Backoff
// utils/retry.js
async function retryWithBackoff(fn, options = {}) {
const maxRetries = options.maxRetries || 3;
const baseDelay = options.baseDelay || 1000;
const maxDelay = options.maxDelay || 10000;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
if (attempt === maxRetries) {
throw error;
}
const delay = Math.min(
baseDelay * Math.pow(2, attempt),
maxDelay
);
console.log(`Retry attempt ${attempt + 1} after ${delay}ms`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
// Usage with SendGrid
async function sendEmail(to, subject, body) {
return await retryWithBackoff(
async () => {
return await sendgrid.send({ to, subject, html: body });
},
{ maxRetries: 3, baseDelay: 2000 }
);
}
Pattern 3: Fallback Providers
// services/emailService.js
class EmailService {
constructor() {
this.providers = [
{ name: 'SendGrid', client: sendgridClient, priority: 1 },
{ name: 'AWS SES', client: sesClient, priority: 2 },
{ name: 'Resend', client: resendClient, priority: 3 }
];
}
async send(email) {
// Try providers in priority order
for (const provider of this.providers) {
try {
const result = await this.tryProvider(provider, email);
console.log(`Email sent via ${provider.name}`);
return result;
} catch (error) {
console.warn(`${provider.name} failed:`, error.message);
// Continue to next provider
}
}
throw new Error('All email providers failed');
}
async tryProvider(provider, email) {
return await retryWithBackoff(
async () => provider.client.send(email),
{ maxRetries: 2 }
);
}
}
module.exports = new EmailService();
Many teams display API status on internal dashboards or status pages. RSS feeds provide a lightweight way to aggregate status updates.
Example: Displaying API status on your internal dashboard
// pages/api/status-feed.js
import Parser from 'rss-parser';
const parser = new Parser();
export default async function handler(req, res) {
try {
// Fetch RSS feed from API Status Check
const feed = await parser.parseURL(
'https://apistatuscheck.com/feeds/custom.xml?apis=stripe,sendgrid,auth0,openai'
);
// Transform feed items to dashboard format
const statusData = feed.items.map(item => ({
api: item.title.split(':')[0].trim(),
status: item.title.toLowerCase().includes('down') ? 'down' : 'operational',
message: item.contentSnippet,
timestamp: new Date(item.pubDate)
}));
res.status(200).json({ statuses: statusData });
} catch (error) {
res.status(500).json({ error: 'Failed to fetch status feed' });
}
}
Frontend dashboard component (React):
// components/ApiStatusDashboard.jsx
import { useEffect, useState } from 'react';
export default function ApiStatusDashboard() {
const [statuses, setStatuses] = useState([]);
useEffect(() => {
async function fetchStatus() {
const res = await fetch('/api/status-feed');
const data = await res.json();
setStatuses(data.statuses);
}
fetchStatus();
const interval = setInterval(fetchStatus, 60000); // Update every minute
return () => clearInterval(interval);
}, []);
return (
<div className="api-status-dashboard">
<h2>Third-Party API Status</h2>
{statuses.map((api, idx) => (
<div key={idx} className={`status-item ${api.status}`}>
<span className="api-name">{api.api}</span>
<span className={`status-badge ${api.status}`}>
{api.status === 'operational' ? 'âś“ Operational' : 'âš Degraded'}
</span>
{api.message && <p className="status-message">{api.message}</p>}
</div>
))}
</div>
);
}
Many APIs publish status pages (e.g., status.stripe.com). However, status pages often update after the outage begins. Direct API monitoring catches issues faster.
Don't rely on just email. Configure:
Regularly test your circuit breakers and backup providers. Don't wait for a real outage to discover your failover logic is broken.
Maintain a living document that maps:
If you expose APIs to customers, monitor them the same way you monitor third-party services. Use the same alerting and incident response workflows.
For most SaaS teams, a paid monitoring tier pays for itself within the first prevented outage.
Free tier limitations:
Paid tier benefits ($9-49/month):
ROI calculation:
Learn more about API Status Check pricing
Third-party API outages are inevitable—but their impact on your SaaS application is entirely within your control.
By implementing proactive monitoring, automated alerts, webhook integrations, and graceful degradation strategies, you transform API outages from business-critical emergencies into minor incidents that resolve automatically.
Quick wins to start today:
Next steps:
Your users will never thank you for preventing outages they never experienced—but they'll definitely remember the ones you didn't.
Try API Status Check — free real-time monitoring for 117+ APIs