Working with APIs: Consumption, Integration, and Error Handling
Best practices for integrating third-party APIs into your applications reliably
APIs Are Everywhere
Modern applications rarely exist in isolation. They integrate with payment processors, email services, analytics platforms, social media APIs, cloud storage, and dozens of other services. How you consume and integrate these APIs significantly impacts your application's reliability, performance, and maintainability.
A poorly integrated API can bring your entire application down when the external service has issues. A well-integrated one degrades gracefully and keeps your users informed.
HTTP Client Best Practices
Use a dedicated HTTP client library rather than raw cURL or low-level functions. In PHP, Laravel's HTTP client (built on Guzzle) provides a clean, fluent API. In JavaScript, Axios or the native fetch API with proper wrappers work well.
// Laravel HTTP client with proper configuration
$response = Http::timeout(10)
->retry(3, 100, function ($exception) {
return $exception instanceof ConnectionException;
})
->withHeaders([
'Authorization' => 'Bearer ' . $apiKey,
'Accept' => 'application/json',
])
->get('https://api.example.com/users');
if ($response->successful()) {
$users = $response->json('data');
} else {
logger()->error('API request failed', [
'status' => $response->status(),
'body' => $response->body(),
]);
}
Always set timeouts to prevent your application from hanging when an external service is slow. Default timeouts of 30 seconds or more are almost always too long — most APIs should respond within 5-10 seconds.
Error Handling and Resilience
External APIs will fail. Networks are unreliable, services have outages, and rate limits get exceeded. Design your integration to handle these failures gracefully:
- Retry with backoff — For transient errors (timeouts, 500 errors), retry the request with exponential backoff. Don't retry client errors (400, 401, 404) — they won't succeed on retry.
- Circuit breaker pattern — After repeated failures, stop making requests for a period and return a cached or default response. This prevents cascading failures and gives the external service time to recover.
- Graceful degradation — If a non-critical API is down (analytics, recommendations), show the page without that data rather than failing entirely.
- Meaningful error messages — Don't show users raw API error responses. Translate them into actionable messages: "Payment could not be processed. Please try again or use a different payment method."
Rate Limiting
Most APIs impose rate limits to prevent abuse. Respect these limits — exceeding them can get your API key revoked. Implement client-side rate limiting that tracks your request count and throttles when approaching the limit. Cache responses to reduce unnecessary requests, and batch operations where the API supports it.
When you do hit a rate limit (HTTP 429), respect the Retry-After header and wait before retrying. Don't hammer the API with retries — that makes the problem worse for everyone.
Caching API Responses
Caching is essential for APIs that you call frequently with similar parameters. Cache responses based on the request URL and parameters, and set TTLs based on how frequently the data changes. User profile data might be cached for minutes, while exchange rates might be cached for seconds.
Be thoughtful about cache invalidation. For webhook-enabled APIs, invalidate cache entries when you receive webhook notifications about data changes. For APIs without webhooks, use short TTLs and accept that data might be slightly stale.
Testing API Integrations
Don't hit real APIs in your automated tests. Use mocking to simulate API responses — both successful and error cases. This makes your tests fast, deterministic, and free from external dependencies. Mock not just the happy path but also error responses, timeouts, and rate limits to verify that your error handling works correctly.
Maintain a separate set of integration tests that hit sandbox/staging API environments to verify that your integration still works with the real API. Run these periodically rather than on every commit.
Share this post: