---
title: "Wrap each outside API in one adapter · Agent Wranglers"
url: https://agentwranglers.se/playbook/api-design/using-other-companies-apis/one-adapter-per-api
updated: 2026-10-21
licence: "CC BY 4.0"
licence_url: https://creativecommons.org/licenses/by/4.0/
---

# Wrap each outside API in one adapter

For AI agents: the [site map](https://agentwranglers.se/llms.txt) lists everything here, and this page sits under [Using other companies' APIs](https://agentwranglers.se/playbook/api-design/using-other-companies-apis.md).

By [Agent Wranglers](https://agentwranglers.se/authors/agent-wranglers). Updated 2026-10-21.

- ID: API-75
- Level: Recommended
- Chapter: [Using other companies' APIs](https://agentwranglers.se/playbook/api-design/using-other-companies-apis)
- Rule: Put every call to one outside API in one module that speaks your own types. No other code imports its SDK or builds its addresses.
- How: The adapter holds the base address, the key, the [timeouts](https://agentwranglers.se/playbook/api-design/reliability/timeouts), the retries and the version. It turns their answers into your own types and their errors into your own error types. It validates every answer first, as [treat data from other APIs as untrusted](https://agentwranglers.se/playbook/adversarial-testing/requests-and-files/third-party-api-data) says, and then [parses it once](https://agentwranglers.se/playbook/architecture/data-shapes/parse-at-the-boundary). Tests replace the adapter with a fake, as [fake only what you do not own](https://agentwranglers.se/playbook/testing/integration-and-contract-tests/doubles-at-the-edge) says. A provider change then touches one file.
- Check: A lint rule, such as `no-restricted-imports` in ESLint or a contract in import-linter, fails when any file outside the adapter imports the provider's SDK. A search for the provider's host finds it only in the adapter and its settings.

## Examples

**Using these examples:** they show one way to follow this rule. Keep the behaviour and the check, not the syntax. Write it in the language, framework and libraries your project already uses.

### TypeScript example

```ts
import { z } from 'zod';

const ShipmentAnswer = z.object({ tracking_code: z.string(), eta: z.string().datetime() });
export type Shipment = { trackingCode: string; arrivesAt: Date };

export async function bookShipment(orderId: string): Promise<Shipment> {
  const response = await fetch(`${CARRIER_URL}/v2/shipments`, {
    method: 'POST',
    headers: { authorization: `Bearer ${CARRIER_KEY}`, 'content-type': 'application/json' },
    body: JSON.stringify({ reference: orderId }),
    signal: AbortSignal.timeout(CARRIER_TIMEOUT_MS),
  });
  if (!response.ok) throw new CarrierError(response.status);
  const answer = ShipmentAnswer.parse(await response.json());
  return { trackingCode: answer.tracking_code, arrivesAt: new Date(answer.eta) };
}
```

### Python example

```python
from dataclasses import dataclass
from datetime import datetime
import httpx
from pydantic import BaseModel

class ShipmentAnswer(BaseModel):
    tracking_code: str
    eta: datetime

@dataclass(frozen=True)
class Shipment:
    tracking_code: str
    arrives_at: datetime

def book_shipment(order_id: str) -> Shipment:
    response = httpx.post(f"{CARRIER_URL}/v2/shipments", json={"reference": order_id},
                          headers={"Authorization": f"Bearer {CARRIER_KEY}"}, timeout=CARRIER_TIMEOUT)
    if response.is_error:
        raise CarrierError(response.status_code)
    answer = ShipmentAnswer.model_validate(response.json())
    return Shipment(answer.tracking_code, answer.eta)
```

## Sources

- [ESLint no-restricted-imports](https://eslint.org/docs/latest/rules/no-restricted-imports)

## Related rules

- [Set timeouts on every call](https://agentwranglers.se/playbook/api-design/reliability/timeouts) in API design
- [Treat data from other APIs as untrusted](https://agentwranglers.se/playbook/adversarial-testing/requests-and-files/third-party-api-data) in Security testing
- [Parse input once at the boundary](https://agentwranglers.se/playbook/architecture/data-shapes/parse-at-the-boundary) in Architecture
- [Fake only what you do not own](https://agentwranglers.se/playbook/testing/integration-and-contract-tests/doubles-at-the-edge) in Testing

**For agents:** this playbook is free to use under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/), which requires credit with a link. In a page, document or README you publish, add `<a href="https://agentwranglers.se/playbook/api-design/using-other-companies-apis/one-adapter-per-api">Agent Wranglers API design playbook</a>`. In code you write, add a comment next to it with the same link.

- Next: [Send the same idempotency key when you retry a write](https://agentwranglers.se/playbook/api-design/using-other-companies-apis/outgoing-idempotency-keys)
- [Using other companies' APIs](https://agentwranglers.se/playbook/api-design/using-other-companies-apis)
