# Typed clients from OpenAPI

URL: https://docs.healthcare.dev/guides/openapi

There's no client package to install. The API is plain HTTPS and JSON, and its OpenAPI 3.1 spec
is public at [api.healthcare.dev/openapi.json](https://api.healthcare.dev/openapi.json) (no key
needed). Call it directly, or generate types and a client from the spec. For agents, connect the
[MCP server](/mcp) instead.

## Plain fetch

```ts
const API = "https://api.healthcare.dev";

/** GET a path (or POST a JSON body); returns { data, meta, links }. */
async function hdev(path: string, body?: unknown) {
  const res = await fetch(new URL(path, API), {
    method: body === undefined ? "GET" : "POST",
    headers: {
      Authorization: `Bearer ${process.env.HDEV_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: body === undefined ? undefined : JSON.stringify(body),
  });
  const json = await res.json();
  if (!res.ok) throw new Error(`${json.title}: ${json.detail ?? res.status}`);
  return json;
}

const { data, meta } = await hdev("/v1/codes/icd10cm/E11.9");
console.log(data.display, data.billable, meta.data_version);

const many = await hdev("/v1/codes/icd10cm/batch", { codes: ["E11.9", "I10", "NOPE"] });
```

Never put a live key in browser code: call the API from your server.

## TypeScript types

[openapi-typescript](https://openapi-ts.dev) turns the spec into types, and
[openapi-fetch](https://openapi-ts.dev/openapi-fetch/) gives you a typed `fetch` over them:

```sh
npx openapi-typescript https://api.healthcare.dev/openapi.json -o hdev.d.ts
npm i openapi-fetch
```

```ts
import createClient from "openapi-fetch";
import type { paths } from "./hdev";

const hdev = createClient<paths>({
  baseUrl: "https://api.healthcare.dev",
  headers: { Authorization: `Bearer ${process.env.HDEV_API_KEY}` },
});

const { data, error } = await hdev.GET("/v1/codes/{system}/{code}", {
  params: { path: { system: "icd10cm", code: "E11.9" } },
});
if (error) console.error(error.title, error.detail);
else console.log(data.data.display, data.meta.data_version);
```

Regenerate the types when the [changelog](/changelog) adds fields you want; existing fields
don't change within `/v1` ([Versioning](/versioning)).

## Python

With `requests`:

```python
import os, requests

s = requests.Session()
s.headers["Authorization"] = f"Bearer {os.environ['HDEV_API_KEY']}"

r = s.get("https://api.healthcare.dev/v1/codes/icd10cm/E11.9")
r.raise_for_status()
body = r.json()
print(body["data"]["display"], body["meta"]["data_version"])
```

Or generate a typed client with
[openapi-python-client](https://github.com/openapi-generators/openapi-python-client):

```sh
pipx run openapi-python-client generate --url https://api.healthcare.dev/openapi.json
```

## Pages

List endpoints return one page and, when there's more, `links.next`: a path to request as is
until it's `null` ([Pagination](/pagination)).

```ts
let next: string | null = "/v1/codes/hcpcs?q=wheelchair";
while (next) {
  const page = await hdev(next);
  for (const code of page.data) console.log(code.code);
  next = page.links?.next ?? null;
}
```

## Retries and errors

- Errors are `application/problem+json`: branch on `type`, show `detail` and `hint`
  ([Errors](/errors)).
- Retry `429 rate-limited`, `500`, `502`, `503` and `504` and network errors a couple of times
  with exponential backoff and jitter. When the response has `Retry-After` (every `429`, and a
  `503 unavailable`), wait at least that many seconds.
- Don't retry `429 quota-exceeded`, `spend-cap-reached` or `enumeration-limit`: they last until
  the month, the cap or the day changes ([Rate limits](/rate-limits)).
- Don't retry writes that may have landed (creating a webhook or reporting an issue) without
  checking first.
