Skilllibrary external-api-client
Standardizes retries, backoff, timeout, idempotency, and circuit-breaker decisions when consuming third-party APIs. Trigger on "call external API", "retry logic", "rate limiting", "circuit breaker", "HTTP client wrapper". Do NOT use for api-schema (designing APIs), auth-patterns (auth implementation), or mcp-protocol (MCP servers).
git clone https://github.com/merceralex397-collab/skilllibrary
T=$(mktemp -d) && git clone --depth=1 https://github.com/merceralex397-collab/skilllibrary "$T" && mkdir -p ~/.claude/skills && cp -r "$T/02-generated-repo-core/external-api-client" ~/.claude/skills/merceralex397-collab-skilllibrary-external-api-client && rm -rf "$T"
02-generated-repo-core/external-api-client/SKILL.mdPurpose
Build reliable API clients that handle real-world network conditions: transient failures, rate limits, auth token refresh, and timeouts. Following patterns from production APIs like Anthropic's demonstrates what mature clients look like.
When to use this skill
Use when:
- Integrating a new third-party API (payment, AI, cloud services)
- Existing API client has reliability issues (random failures, rate limit errors)
- Building a wrapper/SDK around an external service
- API calls are in critical paths (checkout, data sync)
Do NOT use when:
- Calling internal services with guaranteed SLAs (use simpler client)
- One-off scripts where manual retry is acceptable
Operating procedure
-
Set appropriate timeouts (never use defaults blindly):
# Anthropic-style: different timeouts for different operations client = httpx.Client( timeout=httpx.Timeout( connect=5.0, # Connection establishment read=60.0, # Waiting for response (LLM calls are slow!) write=10.0, # Sending request body pool=5.0 # Waiting for connection from pool ) ) -
Implement exponential backoff with jitter:
import random import time def retry_with_backoff(fn, max_retries=3, base_delay=1.0): for attempt in range(max_retries): try: return fn() except (RateLimitError, ServiceUnavailableError) as e: if attempt == max_retries - 1: raise delay = base_delay * (2 ** attempt) + random.uniform(0, 1) time.sleep(delay) -
Handle rate limits explicitly:
def call_api(request): response = client.post("/v1/messages", json=request) if response.status_code == 429: retry_after = int(response.headers.get("retry-after", 60)) raise RateLimitError(f"Rate limited, retry after {retry_after}s") if response.status_code >= 500: raise ServiceUnavailableError("Service temporarily unavailable") response.raise_for_status() return response.json() -
Implement auth token refresh:
class APIClient: def __init__(self, credentials): self._credentials = credentials self._token = None self._token_expires_at = 0 def _ensure_token(self): if time.time() >= self._token_expires_at - 60: # Refresh 1 min early self._token = self._refresh_token() self._token_expires_at = time.time() + 3600 def request(self, method, path, **kwargs): self._ensure_token() headers = {"Authorization": f"Bearer {self._token}"} return self._client.request(method, path, headers=headers, **kwargs) -
Use circuit breaker for cascading failure protection:
# After N consecutive failures, stop calling for cooldown period class CircuitBreaker: def __init__(self, failure_threshold=5, cooldown=30): self.failures = 0 self.last_failure_time = 0 self.threshold = failure_threshold self.cooldown = cooldown def call(self, fn): if self.failures >= self.threshold: if time.time() - self.last_failure_time < self.cooldown: raise CircuitOpenError("Circuit breaker open") self.failures = 0 # Reset for retry try: result = fn() self.failures = 0 return result except Exception: self.failures += 1 self.last_failure_time = time.time() raise -
Ensure idempotency for retryable operations:
# Use idempotency keys for non-idempotent operations def create_payment(amount, idempotency_key=None): key = idempotency_key or str(uuid.uuid4()) response = client.post("/payments", json={"amount": amount}, headers={"Idempotency-Key": key} ) return response.json()
Output defaults
class ExternalAPIClient: """ Client for [Service Name] API Features: - Exponential backoff with jitter (max 3 retries) - Rate limit handling with Retry-After header - Automatic token refresh - Circuit breaker (5 failures = 30s cooldown) - Configurable timeouts (connect: 5s, read: 60s) """ RETRYABLE_STATUS_CODES = {429, 500, 502, 503, 504}
References
Failure handling
- Infinite retry loops: Always set max_retries; log when exhausted
- Rate limit storms: Implement client-side rate limiting; don't rely only on 429 responses
- Auth token refresh race: Use mutex/lock when multiple threads share token
- Timeout too short for operation: Research API's expected latency; AI APIs often need 60s+ read timeout
- Missing idempotency key on retry: Always generate key BEFORE first attempt, reuse on retries