API Integration Best Practices for Enterprise Systems
How to integrate third-party APIs securely, reliably, and at scale.
Jason Overmier
Innovative Prospects Team
API Integration Best Practices
Modern applications rely on dozens of third-party services: payment processors, CRMs, email providers, analytics platforms. Integrating them well separates reliable systems from fragile ones.
Common API Integration Challenges
- Rate limiting (getting blocked for too many requests)
- Authentication failures (expired tokens, rotating keys)
- Data inconsistency (APIs changing without notice)
- Performance bottlenecks (slow downstream services)
- Security risks (exposed credentials, data leaks)
Best Practices
1. Centralize Integration Logic
Don’t scatter API calls throughout your codebase. Create a dedicated integration layer:
// /src/integrations/stripe.ts
class StripeIntegration {
private client: Stripe;
constructor(apiKey: string) {
this.client = new Stripe(apiKey);
}
async createPayment(amount: number, customerId: string) {
// Centralized error handling
// Retry logic
// Logging
}
}
Benefits:
- Single place to update API logic
- Consistent error handling
- Easier testing and mocking
2. Handle Rate Limiting Gracefully
Most APIs have rate limits. Exceed them, and requests fail. Implement exponential backoff:
async function fetchWithRetry(url: string, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch(url);
if (response.status === 429) {
// Too Many Requests - wait and retry
const waitTime = Math.pow(2, i) * 1000; // 1s, 2s, 4s
await new Promise((resolve) => setTimeout(resolve, waitTime));
continue;
}
return response;
} catch (error) {
if (i === maxRetries - 1) throw error;
}
}
}
3. Implement Request Queuing
For high-volume APIs (email, SMS, notifications), use a job queue:
- Redis Queue (Node.js)
- Sidekiq (Ruby)
- Celery (Python)
Benefits:
- Process requests asynchronously
- Retry failed jobs automatically
- Scale workers independently
4. Secure Your Credentials
Never hardcode API keys in your codebase:
# ✅ Good - Environment variables
STRIPE_API_KEY=sk_live_xxxxx
DATABASE_URL=postgresql://...
# ❌ Bad - In code
const apiKey = "sk_live_xxxxx";
Additional security:
- Rotate keys quarterly
- Use different keys for dev/staging/prod
- Implement IP whitelisting where possible
- Use secrets management (AWS Secrets Manager, HashiCorp Vault)
5. Monitor and Alert
You can’t fix what you don’t measure. Track:
- Success/error rates per integration
- Response times (slow APIs indicate issues)
- Rate limit usage (approaching limits?)
- Cost (APIs can get expensive fast)
Tools: Datadog, New Relic, or custom dashboards.
6. Version Your Integrations
APIs change. Prepare for it:
// v1/stripe.ts - Original implementation
// v2/stripe.ts - Updated for new API version
// index.ts - Routes to correct version
When an API provider announces deprecation:
- Create new integration alongside old
- Migrate gradually (canary releases)
- Monitor for differences
- Retire old version once stable
7. Design for Failure
APIs go down. Your app shouldn’t crash with them:
async function getCustomerData(customerId: string) {
try {
return await crmApi.getCustomer(customerId);
} catch (error) {
// Fallback to cache
const cached = await cache.get(`customer:${customerId}`);
if (cached) return cached;
// Graceful degradation
logger.error("CRM API down", { error, customerId });
return { name: "Unknown", email: null }; // Partial data
}
}
8. Validate Incoming Data
Never trust third-party APIs. Validate schemas:
import { z } from "zod";
const CustomerSchema = z.object({
id: z.string(),
email: z.string().email(),
name: z.string().min(1),
});
function validateCustomer(data: unknown) {
return CustomerSchema.parse(data);
}
Benefits:
- Catch breaking changes early
- Prevent runtime errors
- Self-documenting code
9. Implement Idempotency
Network failures cause duplicate requests. Design your integrations to handle them:
// Use idempotency keys
await stripe.paymentIntents.create({
amount: 2000,
currency: "usd",
idempotency_key: `payment_${orderId}`, // Same key = same result
});
10. Document Your Integrations
Maintain an integration catalog:
| Integration | Purpose | Owner | Last Updated | Status |
|---|---|---|---|---|
| Stripe | Payments | Engineering | 2026-01-08 | ✅ Operational |
| SendGrid | Marketing | 2026-01-05 | ⚠️ Degraded |
Common Integration Patterns
1. Sync vs. Async
| Pattern | Use Case | Example |
|---|---|---|
| Sync (blocking) | User-facing, needs immediate response | Payment processing |
| Async (queue) | Background tasks, non-urgent | Email sending, analytics |
2. Webhook Handling
For APIs that push data to you (webhooks):
// Verify webhook signature
function verifyWebhook(signature: string, payload: string) {
const hmac = crypto.createHmac("sha256", WEBHOOK_SECRET);
const digest = hmac.update(payload).digest("hex");
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(digest));
}
// Process idempotently
async function handleWebhook(event: WebhookEvent) {
const exists = await processedEvent.findUnique({
where: { id: event.id },
});
if (exists) return; // Already processed
// Process event
await processEvent(event);
await processedEvent.create({ data: { id: event.id } });
}
3. API Gateway Pattern
For multiple third-party integrations, use a gateway:
Your App → API Gateway → [Stripe, Salesforce, HubSpot, ...]
Benefits:
- Single authentication point
- Centralized rate limiting
- Unified logging/monitoring
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Hardcoded API keys | Quick development, forgot security | Use environment variables, secrets management |
| No retry logic | Assumes perfect uptime | Implement exponential backoff with max retries |
| Scattered API calls | Copy-paste implementation across files | Centralize integration logic in dedicated layer |
| Ignoring rate limits | Didn’t read API docs thoroughly | Track usage, implement request queuing |
| Not validating responses | Trusting third-party data | Schema validation with Zod or similar |
| Missing webhook verification | Assumed secure endpoint | Always verify signature with HMAC |
| No idempotency keys | Network failures cause retries | Use idempotency keys for duplicate prevention |
| Silent API failures | Poor error handling | Implement comprehensive monitoring/alerting |
Tools We Recommend
| Purpose | Tool |
|---|---|
| HTTP Client | Axios, Fetch (Node.js) |
| Job Queues | BullMQ (Node.js), Sidekiq (Ruby) |
| Monitoring | Datadog, Sentry |
| Documentation | Stoplight, Swagger |
| Testing | MSW (Mock Service Worker) |
Need help integrating APIs into your system? We’ve built dozens of integrations across payment, CRM, email, and logistics providers. Let’s talk.