> ## Documentation Index
> Fetch the complete documentation index at: https://docs.get-exo.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Clients and SDKs

> There is no published Exo package yet. The contract is the client: generate from the OpenAPI document, or let your agent use the MCP server.

**Exo does not publish a client library today.** No npm package, no PyPI package, no vendored SDK. Saying otherwise would send you looking for something that does not exist.

What Exo does publish is a curated OpenAPI 3.1 document that every route on this site is generated from, and an MCP server that lets a coding agent call the API without any client at all. Between them, most of what an SDK would give you is already available.

## The contract

```
https://docs.get-exo.com/spec/exo-v1.json
```

64 paths, 80 operations, 135 named schemas, plus an `x-exo-concepts` index naming the objects that make Exo different from a vector store. It is curated rather than dumped, so what is in it is what is supported.

Check it into your repository. Diffing it between releases is the cheapest possible upgrade review, and because [`/v1` evolves additively](/platform/versioning), the diff should only ever grow.

## Generating a client

The document is plain OpenAPI 3.1, so the standard generators work. These are the commands, not an endorsement: pick whichever fits your build.

<CodeGroup>
  ```bash TypeScript theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
  # types only, no runtime dependency
  npx openapi-typescript https://docs.get-exo.com/spec/exo-v1.json \
    -o src/exo.d.ts
  ```

  ```bash Python theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
  # pydantic models from the schemas
  datamodel-codegen \
    --url https://docs.get-exo.com/spec/exo-v1.json \
    --input-file-type openapi \
    --output exo_models.py
  ```

  ```bash Any language theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
  # full client via openapi-generator
  openapi-generator-cli generate \
    -i https://docs.get-exo.com/spec/exo-v1.json \
    -g <language> \
    -o ./exo-client
  ```
</CodeGroup>

Three things to configure on whatever you generate:

* **Auth.** The document declares two equivalent security schemes: the `X-Exo-API-Key` header and `Authorization: Bearer`. Most generators will wire one of them; either is correct.
* **The subject header.** `X-Exo-Subject` is not declared as an operation parameter, so a generated client will not expose it. Add it to your wrapper as a per-call header. See [the overview](/api-reference/overview).
* **Idempotency.** `Idempotency-Key` is likewise not a declared parameter. If your wrapper retries automatically, it must send a stable key or the retry is a second write. See [Idempotency](/platform/idempotency).

Those three are the whole gap between a generated client and a hand-written one, which is also why a thin wrapper is usually enough.

## A wrapper worth 20 lines

Most of what an SDK does for you is: attach the key, raise on a problem body, and surface the request id. That is small enough to own.

<CodeGroup>
  ```python Python theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
  import httpx


  class ExoError(RuntimeError):
      def __init__(self, problem: dict) -> None:
          self.code = problem.get("code", "unknown")
          self.request_id = problem.get("requestId", "")
          super().__init__(f"{self.code}: {problem.get('detail', '')} [{self.request_id}]")


  class Exo:
      def __init__(self, api_key: str, subject: str | None = None) -> None:
          headers = {"X-Exo-API-Key": api_key}
          if subject:
              headers["X-Exo-Subject"] = subject
          self._client = httpx.Client(
              base_url="https://api.get-exo.com", headers=headers, timeout=30.0
          )

      def call(self, method: str, path: str, **kwargs):
          response = self._client.request(method, path, **kwargs)
          if response.is_error:
              raise ExoError(response.json())
          return response.json() if response.content else None

      def retrieve(self, query: str, top_k: int = 8):
          return self.call("POST", "/v1/retrieve", json={"query": query, "topK": top_k})
  ```

  ```typescript TypeScript theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
  export class ExoError extends Error {
    constructor(readonly problem: Record<string, unknown>) {
      super(`${problem.code}: ${problem.detail} [${problem.requestId}]`);
    }
  }

  export class Exo {
    constructor(
      private readonly apiKey: string,
      private readonly subject?: string,
    ) {}

    async call(method: string, path: string, body?: unknown) {
      const headers: Record<string, string> = { "X-Exo-API-Key": this.apiKey };
      if (this.subject) headers["X-Exo-Subject"] = this.subject;
      if (body) headers["Content-Type"] = "application/json";

      const response = await fetch(`https://api.get-exo.com${path}`, {
        method,
        headers,
        body: body ? JSON.stringify(body) : undefined,
      });
      if (!response.ok) throw new ExoError(await response.json());
      return response.status === 204 ? null : response.json();
    }

    retrieve(query: string, topK = 8) {
      return this.call("POST", "/v1/retrieve", { query, topK });
    }
  }
  ```

  ```bash cURL theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
  # the same thing, as a shell function
  exo() {
    local method="$1" path="$2" body="${3:-}"
    curl -sS -X "$method" "https://api.get-exo.com${path}" \
      -H "X-Exo-API-Key: $EXO_KEY" \
      ${body:+-H "Content-Type: application/json" -d "$body"}
  }

  exo POST /v1/retrieve '{"query": "Where did we land on pricing?", "topK": 8}'
  ```
</CodeGroup>

Add [rate-limit backoff](/platform/rate-limits) and [pagination](/platform/pagination) when you need them; both are a handful of lines on top of this.

## If your client is an agent

For anything running inside Claude Code, the Claude app, Codex or ChatGPT, you do not need a client at all. Exo hosts an MCP server, and the agent calls the API through it.

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
claude mcp add exo --transport http https://api.get-exo.com/mcp/ \
  --header "Authorization: Bearer exo_..."
```

See [Integrations](/integrations) for the per-client setup, and [MCP tool reference](/integrations/mcp-tools) for which tool maps to which route.

## Reading these docs from an agent

Every page here is available as Markdown by appending `.md` to its URL, and the documentation itself is an MCP server:

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
claude mcp add --transport http exo-docs https://docs.get-exo.com/mcp
```

See [Read these docs from your agent](/integrations/docs-mcp).

## When there is an SDK

There will be a published client eventually. When there is, it will be generated from the same document linked above, so anything you build against the contract now keeps working. This page will say so plainly rather than quietly changing.

## Next

<Columns cols={2}>
  <Card title="Versioning and stability" icon="git-branch" href="/platform/versioning">
    Why generating against the contract is safe.
  </Card>

  <Card title="Integrations" icon="plug" href="/integrations">
    Connect an agent instead of writing a client.
  </Card>
</Columns>
