Skip to content
mager-bench1.3

GPT-5.6 Sol × api-client

8.7

Class design + error handling + type hints + docs

correctness 9.0quality 8.0documentation 9.014062ms
$ cat api-client.prompt — what the model was asked
Write a Python class `APIClient` that wraps the `requests` library for a REST API. It should: (1) accept a base_url and optional api_key in __init__, (2) have a `get(path, params=None)` method and a `post(path, data)` method, (3) raise a custom `APIError` exception with the status code and message on non-2xx responses, (4) include type hints throughout, (5) have a complete docstring on the class and each public method. Show a usage example at the bottom.

run 1 of 1

8.7
correctness 9.0quality 8.0documentation 9.014062ms

# judge: The implementation cleanly satisfies the core requirements with proper error handling, typing, sessions, and docstrings, though it lacks timeout/session-closing support and the example may fail against the placeholder URL without catching transport errors.

$ cat codex-cli/gpt-5.6-sol.api-client.r1.response
```python
from typing import Any, Mapping, Optional

import requests


class APIError(Exception):
    """Raised when the API returns a non-successful HTTP response."""

    def __init__(self, status_code: int, message: str) -> None:
        """Initialize the error with an HTTP status code and message."""
        self.status_code = status_code
        self.message = message
        super().__init__(f"API request failed ({status_code}): {message}")


class APIClient:
    """A lightweight client for making authenticated REST API requests.

    Args:
        base_url: The root URL of the API, such as
            ``"https://api.example.com/v1"``.
        api_key: Optional bearer token used to authenticate requests.
    """

    def __init__(self, base_url: str, api_key: Optional[str] = None) -> None:
        """Initialize the API client.

        Args:
            base_url: The root URL of the API.
            api_key: Optional bearer token used for authentication.
        """
        self.base_url = base_url.rstrip("/")
        self.session = requests.Session()

        if api_key is not None:
            self.session.headers["Authorization"] = f"Bearer {api_key}"

        self.session.headers["Accept"] = "application/json"

    def get(
        self,
        path: str,
        params: Optional[Mapping[str, Any]] = None,
    ) -> Any:
        """Send a GET request to an API endpoint.

        Args:
            path: Endpoint path relative to the base URL.
            params: Optional query-string parameters.

        Returns:
            The decoded JSON response.

        Raises:
            APIError: If the API returns a non-2xx response.
            requests.RequestException: If the request cannot be completed.
            requests.JSONDecodeError: If the response is not valid JSON.
        """
        response = self.session.get(self._url(path), params=params)
        self._raise_for_api_error(response)
        return response.json()

    def post(self, path: str, data: Mapping[str, Any]) -> Any:
        """Send a POST request containing JSON data to an API endpoint.

        Args:
            path: Endpoint path relative to the base URL.
            data: Mapping to serialize as the JSON request body.

        Returns:
            The decoded JSON response.

        Raises:
            APIError: If the API returns a non-2xx response.
            requests.RequestException: If the request cannot be completed.
            requests.JSONDecodeError: If the response is not valid JSON.
        """
        response = self.session.post(self._url(path), json=data)
        self._raise_for_api_error(response)
        return response.json()

    def _url(self, path: str) -> str:
        return f"{self.base_url}/{path.lstrip('/')}"

    @staticmethod
    def _raise_for_api_error(response: requests.Response) -> None:
        if 200 <= response.status_code < 300:
            return

        try:
            payload = response.json()
            message = (
                payload.get("message", response.text)
                if isinstance(payload, dict)
                else response.text
            )
        except requests.exceptions.JSONDecodeError:
            message = response.text

        raise APIError(response.status_code, message or response.reason)


# Usage example
if __name__ == "__main__":
    client = APIClient(
        base_url="https://api.example.com/v1",
        api_key="your-api-key",
    )

    try:
        users = client.get("/users", params={"limit": 10})
        created_user = client.post(
            "/users",
            data={"name": "Ada Lovelace", "email": "ada@example.com"},
        )
        print(users)
        print(created_user)
    except APIError as error:
        print(f"Request failed: {error}")
```