# SDKs

> Use the official OpenAI SDKs for JavaScript and Python against Cairva, including the cairva metering field.

Source: https://cairva.xyz/docs/sdks

Cairva doesn't ship its own SDK. Use the official OpenAI SDKs: the gateway speaks their format, so you change three things and keep the rest of your code.

| Setting | Value |
| --- | --- |
| Base URL | `https://cairva.xyz/v1` |
| API key | Your Cairva key, `cv_live_…` or `cv_test_…` |
| Model | The id from `GET /v1/models` |

## JavaScript and TypeScript

```bash
npm install openai
```

```javascript
import OpenAI from "openai";

const client = new OpenAI({
  apiKey: process.env.CAIRVA_API_KEY,
  baseURL: "https://cairva.xyz/v1",
});

const completion = await client.chat.completions.create({
  model: "gemma4:e2b",
  messages: [{ role: "user", content: "Summarise this ticket in one line." }],
  max_tokens: 200,
});

console.log(completion.choices[0].message.content);
```

The SDK keeps fields it doesn't know about on the object, so `completion.cairva` holds Cairva's metering details. In TypeScript, read it through a narrow type of your own:

```typescript
type CairvaMeta = {
  request_id: string;
  allocation_id: string;
  service_mode: "live" | "sandbox";
  usage_source: "provider_reported" | "sandbox_estimate" | "pending";
  remaining: { input: number; output: number };
};

function cairvaMeta(response: object): CairvaMeta | undefined {
  return "cairva" in response ? (response.cairva as CairvaMeta) : undefined;
}

console.log(cairvaMeta(completion)?.remaining.output);
```

## Python

```bash
pip install openai
```

```python
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["CAIRVA_API_KEY"],
    base_url="https://cairva.xyz/v1",
)

completion = client.chat.completions.create(
    model="gemma4:e2b",
    messages=[{"role": "user", "content": "Summarise this ticket in one line."}],
    max_tokens=200,
)

print(completion.choices[0].message.content)
print(completion.model_extra["cairva"]["remaining"])
```

The Python SDK puts extra response fields in `model_extra`, so the metering object is `completion.model_extra["cairva"]`. On a streamed chunk it's `chunk.model_extra["cairva"]` on the final chunk.

## Errors in the SDKs

Cairva errors arrive as the SDK's normal API errors. The product code is on the error:

```javascript JavaScript
try {
  await client.chat.completions.create({ model: "gemma4:e2b", messages });
} catch (err) {
  if (err instanceof OpenAI.APIError && err.code === "allocation_exhausted") {
    // Out of allowance: alert someone, don't retry.
  }
  throw err;
}
```

```python Python
import openai

try:
    client.chat.completions.create(model="gemma4:e2b", messages=messages)
except openai.APIStatusError as err:
    if err.code == "allocation_exhausted":
        pass  # Out of allowance: alert someone, don't retry.
    raise
```

Both SDKs retry `429` and `5xx` responses on their own and honour `Retry-After`. Leave that on. Don't retry `402` or `403`. See [Errors](https://cairva.xyz/docs/errors).

## Other clients

Any HTTP client works. Send `Authorization: Bearer <key>` and JSON bodies to the endpoints in the [API reference](https://cairva.xyz/docs/chat-completions). Tools built on the OpenAI SDKs usually accept a custom base URL and key the same way. Check that they don't depend on tool calling or structured output, which Cairva doesn't support. See [OpenAI compatibility](https://cairva.xyz/docs/compatibility).
