# C# / .NET
URL: /docs/sdks/csharp

The official tachyon-sdk client for .NET.



```bash
dotnet add package TachyonSdk
```

Targets .NET 8 (LTS). Zero third-party dependencies — just
`System.Net.Http` and `System.Text.Json`, both in the BCL.

## Quickstart [#quickstart]

```csharp
using Tachyon.Sdk;

var client = new TachyonClient(new TachyonClientOptions
{
    Url = "http://localhost:8108",
    ApiKey = "my-admin-key",
});

await client.Collections.CreateAsync(new CollectionSchema
{
    Name = "products",
    Fields =
    [
        new FieldSchema { Name = "title", Type = FieldType.Text },
        new FieldSchema { Name = "brand", Type = FieldType.Keyword, Facet = true },
        new FieldSchema { Name = "price", Type = FieldType.Int, Filter = true, Sort = true },
    ],
});

var collection = client.Collection("products");
await collection.Documents.IndexAsync(
    new Document { ["id"] = "1", ["title"] = "Wireless Mouse", ["brand"] = "Logitech", ["price"] = 2999 },
    new Document { ["id"] = "2", ["title"] = "Mechanical Keyboard", ["brand"] = "Razer", ["price"] = 8999 });

var results = await collection.SearchAsync(new SearchParams { Q = "wireless mouse" });
foreach (var hit in results.Hits)
{
    Console.WriteLine($"{hit.Document["title"]} {hit.TextMatch}");
}
```

`Document` is a type alias for `System.Text.Json.Nodes.JsonObject` declared
via a `global using` inside the package — aliases don't cross assembly
boundaries in C#, so add the same line to your own project if you want to
write `Document` instead of `JsonObject`:

```csharp
global using Document = System.Text.Json.Nodes.JsonObject;
```

Or just use `JsonObject` directly; it's the exact same type either way.

## Client options [#client-options]

```csharp
var client = new TachyonClient(new TachyonClientOptions
{
    Url = "http://localhost:8108",
    ApiKey = "...",                          // admin key (read/write) or search key (read-only)
    Timeout = TimeSpan.FromSeconds(15),       // default 15s; ignored if HttpClient is supplied
    Headers = new Dictionary<string, string> { ["X-Custom"] = "value" },
    HttpClient = myHttpClient,                // override transport (testing, custom pooling)
});
```

Every method takes an optional trailing `CancellationToken`, in keeping with
.NET convention.

## Collections [#collections]

```csharp
client.Collections.CreateAsync(schema);   // POST   /collections
client.Collections.ListAsync();           // GET    /collections
client.Collections.RetrieveAsync(name);   // GET    /collections/{name}
client.Collections.DeleteAsync(name);     // DELETE /collections/{name}
```

`FieldSchema`'s optional attributes (`Facet`, `Filter`, `Sort`, `Index`,
`Optional`, `Boost`) are nullable, so leaving them unset doesn't override
the server's own default.

## Documents [#documents]

```csharp
var collection = client.Collection("products");

collection.Documents.IndexAsync(doc1, doc2, doc3);  // POST   /collections/{name}/documents (upsert by id, params array)
collection.Documents.RetrieveAsync(id);             // GET    /collections/{name}/documents/{id}
collection.Documents.DeleteAsync(id);               // DELETE /collections/{name}/documents/{id}
```

`IndexAsync` always resolves at the HTTP level even if individual documents
are rejected — check `NumFailed` and `Results` on the response.

## Search [#search]

```csharp
await collection.SearchAsync(new SearchParams
{
    Q = "wireless mouse",
    QueryBy = ["title", "description"],
    Filter = "brand:=Logitech && price:<5000",
    Sort = "_text_match:desc,price:asc",
    Facet = ["brand", "year"],
    Limit = 20,
    Offset = 0,
    Prefix = true,
    TypoTolerance = true,
    MatchMode = MatchMode.All, // or MatchMode.Any
});
```

`FoundIsExact` on the response is `false` once block-max WAND pruning has
skipped part of a term's postings for a broad query — at that point `Found`
and facet counts are a lower bound, not an exact count.

## Autocomplete [#autocomplete]

```csharp
await collection.SuggestAsync(new SuggestParams { Q = "wir", Limit = 5 });
```

## Analytics [#analytics]

```csharp
await client.Analytics.TopAsync(new AnalyticsQueryParams { Collection = "products", Limit = 10 });
await client.Analytics.ZeroResultsAsync(new AnalyticsQueryParams { Collection = "products" });
await client.Analytics.LatencyAsync();
```

Analytics are in-memory only and reset when the server restarts.

## Operations [#operations]

```csharp
await client.HealthAsync();   // GET /health — no API key required
await client.MetricsAsync();  // GET /metrics — Prometheus exposition format, returned as text
```

## Errors [#errors]

Every non-2xx response throws a `TachyonException` subclass carrying the
server's stable `Code` and the HTTP `StatusCode`:

```csharp
try
{
    await client.Collections.RetrieveAsync("does-not-exist");
}
catch (TachyonNotFoundException e)
{
    Console.WriteLine($"{e.Code} {e.StatusCode} {e.Message}"); // collection_not_found 404 ...
}
catch (TachyonException e)
{
    // any other API error
}
```

| Exception                        | Status | Codes                                                                 |
| -------------------------------- | ------ | --------------------------------------------------------------------- |
| `TachyonRequestException`        | 400    | `invalid_schema`, `invalid_document`, `invalid_query`, `invalid_json` |
| `TachyonAuthenticationException` | 401    | `unauthorized`                                                        |
| `TachyonAuthorizationException`  | 403    | `forbidden`                                                           |
| `TachyonNotFoundException`       | 404    | `collection_not_found`, `document_not_found`                          |
| `TachyonConflictException`       | 409    | `collection_exists`                                                   |
| `TachyonServerException`         | 5xx    | `corrupt_data`, `io_error`, `internal_error`                          |

Network failures and timeouts throw `TachyonConnectionException` /
`TachyonTimeoutException` instead, since there's no server response to read
a code from.

## Source [#source]

[`csharp/`](https://github.com/adikeshri/tachyon-sdk/tree/main/csharp) in the
`tachyon-sdk` repo, including the mocked unit suite (`Tachyon.Sdk.Tests`) and
a separate integration test project that runs against a real server.
