> For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/resources/sdk.md).

# SDK Reference

**Titan provides official SDKs for TypeScript and Rust.** Both connect to Titan Direct over WebSocket using MessagePack encoding with optional compression.

***

## TypeScript SDK

**A high-level client with built-in connection management, compression negotiation, and full type safety.**

* **Package:** `@titanexchange/sdk-ts`
* **Source:** [github.com/Titan-Pathfinder/titan-sdk-ts](https://github.com/Titan-Pathfinder/titan-sdk-ts)
* **Node.js:** >=18.19

### Installation

```bash
npm install @titanexchange/sdk-ts
```

### Connecting

```typescript
import { V1Client } from '@titanexchange/sdk-ts';

// The SDK automatically negotiates compression (zstd, brotli, gzip, or none)
const url = `wss://${process.env.TITAN_ENDPOINT}/ws?auth=${process.env.TITAN_API_KEY}`;
const client = await V1Client.connect(url);
```

**Connection state:**

* **`client.closed`** — `boolean`, `true` if the connection is closed.
* **`client.listen_closed()`** — Returns a `Promise` that resolves with the close event.
* **`client.close()`** — Gracefully closes the connection.

### API methods

* **`client.getInfo()`** → `ServerInfo` — Protocol version and server settings.
* **`client.newSwapQuoteStream(params)`** → `{ response, stream, streamId }` — Opens a streaming quote.
* **`client.stopStream(streamId)`** → `StopStreamResponse` — Stops a stream by ID.
* **`client.getVenues(params?)`** → `VenueInfo` — Lists available on-chain venues.
* **`client.listProviders(params?)`** → `ProviderInfo[]` — Lists active quote providers.
* **`client.getSwapPrice(params)`** → `SwapPrice` — One-shot price check without streaming.

### Streaming quotes

```typescript
import bs58 from 'bs58';

const { stream, streamId } = await client.newSwapQuoteStream({
  swap: {
    inputMint: bs58.decode('So11111111111111111111111111111111111111112'),
    outputMint: bs58.decode('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'),
    amount: 1_000_000_000n, // 1 SOL — must be BigInt
    slippageBps: 50,
  },
  transaction: {
    userPublicKey: bs58.decode('YOUR_WALLET_PUBLIC_KEY'),
  },
});

// Async iterator — yields SwapQuotes on each update
for await (const quotes of stream) {
  // Use metadata.ExpectedWinner for the best slippage-adjusted route
  const winner = quotes.metadata?.ExpectedWinner;
  const best = winner && quotes.quotes[winner];

  if (best?.instructions?.length) {
    console.log(`Best: ${winner} — ${best.outAmount} out`);
  }
}
```

### Stopping a stream

```typescript
// Method 1: via client
await client.stopStream(streamId);

// Method 2: via stream — calls stopStream() internally
await stream.cancel('done');
```

### Types

```typescript
import { types } from '@titanexchange/sdk-ts';

types.v1.SwapQuoteRequest;
types.v1.SwapParams;
types.v1.TransactionParams;
```

### Error handling

All error classes are exported from the SDK:

* **`ConnectionClosed`** — WebSocket connection closed. Properties: `code`, `reason`, `wasClean`.
* **`ConnectionError`** — WebSocket error event. Property: `cause`.
* **`ErrorResponse`** — Server rejected a request. Property: `response` (with `code`, `message`, `requestId`).
* **`StreamError`** — Stream ended with an error. Properties: `streamId`, `errorCode`, `errorMessage`.
* **`ProtocolError`** — Implementation bug — **report to Titan.** Properties: `reason`, `data`.

### Reconnection

**The SDK does not include built-in reconnect logic.** Handle reconnection manually:

```typescript
client.listen_closed().then(async (event) => {
  if (!event.wasClean) {
    // Reconnect and re-establish streams
    const newClient = await V1Client.connect(url);
    // Re-open your streams on newClient
  }
});
```

See [Error Handling & Reconnect](/titan/developer-doc/swap-api/guides/error-handling.md) for backoff strategies.

### Browser usage

```typescript
import { V1Client } from '@titanexchange/sdk-ts/browser';
```

{% hint style="warning" %}
**Do not expose your API key in client-side code.** Use a middleware proxy that accepts user connections, validates authentication, and forwards to Titan with the API key server-side. See `examples/middleware.ts` in the SDK repository.
{% endhint %}

### Key details

* **BigInt amounts** — Pass `amount` as `BigInt` (e.g. `1_000_000_000n`). Numbers >= 2^32 may be encoded as float64, **which the server rejects.**
* **`quotes` is a map** — `SwapQuotes.quotes` is `Record<string, SwapRoute>`, keyed by provider ID. Not every provider appears in every update.
* **`num_quotes` uses snake\_case** — In `QuoteUpdateParams`, the field is `num_quotes` (not `numQuotes`).

**Logging BigInt values:**

```typescript
JSON.stringify(data, (key, value) => {
  if (typeof value === 'bigint') return value.toString() + 'n';
  if (value instanceof Uint8Array) return `<Uint8Array: ${value.length} bytes>`;
  return value;
}, 2);
```

***

## Rust SDK

**Low-level type definitions and MessagePack codec — you manage the WebSocket connection yourself.**

* **Crates:** [crates.io/search?q=titan-api-types](https://crates.io/search?q=titan-api-types)
* **`titan-api-types`** — Type definitions for all WebSocket request and response messages.
* **`titan-api-codec`** — MessagePack encoding/decoding with compression support (zstd, brotli, gzip).

### Installation

```toml
[dependencies]
titan-api-types = "5"
titan-api-codec = "1.2"
```

{% hint style="info" %}
There is **no high-level client** in the Rust SDK. You manage the WebSocket connection using `tokio-tungstenite` (or any async WebSocket library) and use the codec for serialization.
{% endhint %}

### Connecting

```rust
use titan_api_codec::codec::{ws::v1::ClientCodec, Codec};
use titan_api_types::ws::v1;
use tokio_tungstenite::{
    connect_async,
    tungstenite::{
        client::IntoClientRequest,
        http::header::{AUTHORIZATION, SEC_WEBSOCKET_PROTOCOL},
        http::HeaderValue,
    },
};

let mut request = url.into_client_request()?;

// Set protocol negotiation header
let protocols = HeaderValue::from_str(
    &v1::WEBSOCKET_SUBPROTOCOLS.join(", ")
)?;
request.headers_mut().insert(SEC_WEBSOCKET_PROTOCOL, protocols);

// Set auth header
let bearer = HeaderValue::from_str(&format!("Bearer {}", token))?;
request.headers_mut().insert(AUTHORIZATION, bearer);

// Connect and create codec from negotiated protocol
let (stream, response) = connect_async(request).await?;
let protocol_str = response
    .headers()
    .get(SEC_WEBSOCKET_PROTOCOL)
    .and_then(|v| v.to_str().ok())
    .unwrap();
let codec = ClientCodec::from_str(protocol_str)?;

let (sink, stream) = stream.split();
```

### Sending requests

```rust
use titan_api_types::ws::v1::*;
use titan_api_codec::codec::Codec;

let encoder = codec.encoder();
let decoder = codec.decoder();

// GetInfo request
let request = ClientRequest {
    id: 0,
    data: RequestData::GetInfo(GetInfoRequest::default()),
};

// Encode to MessagePack and send as binary frame
let bytes = encoder.encode(&request)?;
sink.send(Message::Binary(bytes)).await?;
```

### Streaming quotes

```rust
let request = ClientRequest {
    id: 1,
    data: RequestData::NewSwapQuoteStream(SwapQuoteRequest {
        swap: SwapParams {
            input_mint: sol_mint,
            output_mint: usdc_mint,
            amount: 1_000_000_000u64,
            slippage_bps: Some(50),
            ..Default::default()
        },
        transaction: TransactionParams {
            user_public_key: wallet,
            ..Default::default()
        },
        update: None,
    }),
};
```

### Processing responses

```rust
while let Some(msg) = stream.next().await {
    let msg = msg?;
    if let Message::Binary(data) = msg {
        let server_msg: ServerMessage = decoder.decode(data.into())?;
        match server_msg {
            ServerMessage::Response(resp) => {
                // Handle RPC response
            }
            ServerMessage::StreamData(data) => {
                if let StreamDataPayload::SwapQuotes(quotes) = data.payload {
                    for (provider, route) in &quotes.quotes {
                        println!("{}: {} out", provider, route.out_amount);
                    }
                }
            }
            ServerMessage::Error(err) => {
                eprintln!("Error {}: {}", err.code, err.message);
            }
            ServerMessage::StreamEnd(end) => {
                println!("Stream {} ended", end.id);
            }
        }
    }
}
```

### Field naming

The Rust SDK uses standard **snake\_case** field names. Serde handles the conversion to camelCase on the wire:

* `input_mint` → `inputMint`
* `output_mint` → `outputMint`
* `slippage_bps` → `slippageBps`
* `user_public_key` → `userPublicKey`
* `only_direct_routes` → `onlyDirectRoutes`

### Reconnection

**No built-in reconnect logic.** Handle connection drops and re-establish streams manually, same as the TypeScript SDK.

### Dependencies

* **`tokio-tungstenite`** — Async WebSocket client.
* **`rmp-serde`** — MessagePack serialization.
* **`zstd`** — Zstandard compression.
* **`brotli`** — Brotli compression.
* **`flate2`** — Gzip compression.
* **`five8` / `five8_const`** — Base58 pubkey encoding.

***

## Related pages

* [Quickstart](/titan/developer-doc/swap-api/quickstart.md) — End-to-end integration example
* [Stream & Execute a Swap](/titan/developer-doc/swap-api/guides/stream-and-execute.md) — Full guide with transaction building
* [Wire Protocol](/titan/developer-doc/swap-api/reference/wire-protocol.md) — Encoding rules and message envelopes
* [Error Codes](/titan/developer-doc/swap-api/reference/error-codes.md) — Error codes and SDK error classes
