DEV Community

Chris Lee
Chris Lee

Posted on

Practical Tip– Centralize API Error Handling with a Wrapper

When working with multiple external services, each API can raise different exceptions, return inconsistent error formats, or require specific retry logic. By wrapping all calls in a single helper function you eliminate duplication, make debugging easier, and enforce a uniform response shape across your codebase. This also simplifies swapping out one service for another, since the rest of your application interacts only with the wrapper’s consistent interface.

import requests
import time
import logging

def call_api(url, params=None, max_retries=3, backoff=2):
    """
    Sends a GET request and handles common errors.
    Returns a dict: {'success': bool, 'data': <payload|None>, 'error': <str|None>}
    """
    for attempt in range(1, max_retries + 1):
        try:
            response = requests.get(url, params=params, timeout=10)
            response.raise_for_status()          # HTTP errors (4xx/5xx)
            return {'success': True, 'data': response.json(), 'error': None}
        except (requests.RequestException, ValueError) as exc:
            logging.warning(f"API call failed (attempt {attempt}/{max_retries}): {exc}")
            if attempt < max_retries:
                time.sleep(backoff ** attempt)   # exponential backoff
            else:
                return {'success': False, 'data': None, 'error': str(exc)}
Enter fullscreen mode Exit fullscreen mode

Using this call_api wrapper, every integration in your project calls the same function, automatically retries on transient failures, logs meaningful context, and always returns a predictable structure. This reduces boilerplate, prevents overlooked error cases, and makes your code more robust and maintainable.

Top comments (0)