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

# SDKs – Python and Node

> Official Python and TypeScript/Node.js SDKs for the HydraDB API.

The SDKs wrap every endpoint in the [API Reference](/api-reference/v2) with typed methods and IDE autocomplete. They automatically set the `API-Version: 2` header on every request  -  you do not need to send it manually.

## Installation

The SDKs ship as major-version bumps of the same packages used for v1. Pin to `>=2.0.0` to use the latest method surface.

<CodeGroup>
  ```bash Python SDK theme={"dark"}
  pip install "hydradb-sdk>=2,<3"
  ```

  ```bash TypeScript / Node.js theme={"dark"}
  npm install @hydradb/sdk@^2
  ```
</CodeGroup>

## Client setup

<CodeGroup>
  ```python Python SDK theme={"dark"}
  import os
  from hydra_db import HydraDB

  client = HydraDB(token=os.environ["HYDRA_DB_API_KEY"])
  ```

  ```python Python (Async) theme={"dark"}
  import os
  from hydra_db import AsyncHydraDB

  async_client = AsyncHydraDB(token=os.environ["HYDRA_DB_API_KEY"])
  ```

  ```typescript TypeScript SDK theme={"dark"}
  import { HydraDBClient } from "@hydradb/sdk";

  const client = new HydraDBClient({
    token: process.env.HYDRA_DB_API_KEY,
  });
  ```
</CodeGroup>

<Info>
  **Python:** Both synchronous (`HydraDB`) and asynchronous (`AsyncHydraDB`) clients are available. They share an identical surface  -  choose based on your application's concurrency model.
</Info>

## Versioning

The SDKs automatically include `API-Version: 2` on every outbound request. Server-side routing resolves to the matching routes; the response includes an `X-API-Version: 2` header echoing the resolved version.

You can verify which version your client is using by inspecting the response headers in any SDK call.

## Naming conventions

The REST API uses **snake\_case** for all request and response fields, and both SDKs preserve those field names for request/response objects. TypeScript only camelCases multi-word **method names**.

|                     | Method naming                    | Parameter field naming | Example                                           |
| ------------------- | -------------------------------- | ---------------------- | ------------------------------------------------- |
| **Raw HTTP / cURL** | -                                | snake\_case            | `database`, `collection`, `query_by`              |
| **Python SDK**      | snake\_case                      | snake\_case            | method: `query()`, params: `database`, `query_by` |
| **TypeScript SDK**  | camelCase for multi-word methods | snake\_case            | method: `query()`, params: `database`, `query_by` |

<Info>
  **Use snake\_case request fields in TypeScript examples too** (e.g., `client.context.list({ database: "...", page_size: 50 })`). The SDK sends the same names on the wire, matching the OpenAPI schema.

  The Python SDK also uses snake\_case throughout (e.g., `client.context.list(database="...", page_size=50)`).

  `database` and `collection` are the current field names (formerly `tenant_id` and `sub_tenant_id`). The old names remain accepted as deprecated aliases for full backward compatibility.
</Info>

Your IDE's autocomplete and type checking work directly off the API contract  -  if a field is optional in the API, it's optional in the SDK.

## SDK method structure

SDK methods are grouped under three top-level namespaces  -  one per `/api-reference/v2` group:

| URL prefix                      | SDK group          | Purpose                                                                                                   |
| ------------------------------- | ------------------ | --------------------------------------------------------------------------------------------------------- |
| `/context/*` and `/context`     | `client.context`   | Ingest, status, fetch, list, delete, graph relations                                                      |
| `/query`                        | `client.query`     | Unified retrieval over knowledge, memories, or both                                                       |
| `/databases/*` and `/databases` | `client.databases` | Create, list, delete, status, collections, stats. Legacy `/tenants/*` paths remain as deprecated aliases. |

## Method reference

### Context

`client.context.*` covers every flow around content lifecycle  -  document uploads, app sources, memories, polling, fetching, listing, deletion, and graph inspection.

| Method                       | Endpoint                 |
| ---------------------------- | ------------------------ |
| `client.context.ingest()`    | `POST /context/ingest`   |
| `client.context.status()`    | `GET /context/status`    |
| `client.context.inspect()`   | `GET /context/inspect`   |
| `client.context.list()`      | `POST /context/list`     |
| `client.context.delete()`    | `DELETE /context`        |
| `client.context.relations()` | `GET /context/relations` |

### Query

A single method covers all retrieval. Pick `type` (`"knowledge"`, `"memory"`, `"all"`) and `query_by` (`"hybrid"`, `"text"`) to control behavior.

| Method           | Endpoint      |
| ---------------- | ------------- |
| `client.query()` | `POST /query` |

### Databases

| Method                           | Endpoint                     |
| -------------------------------- | ---------------------------- |
| `client.databases.create()`      | `POST /databases`            |
| `client.databases.list()`        | `GET /databases`             |
| `client.databases.delete()`      | `DELETE /databases`          |
| `client.databases.status()`      | `GET /databases/status`      |
| `client.databases.collections()` | `GET /databases/collections` |
| `client.databases.stats()`       | `GET /databases/stats`       |

The method-reference tables above use the Python (snake\_case) method names. TypeScript keeps the same method names but camelCases any that are multi-word (for example, the connector method `list_resources()` in Python is `listResources()` in TypeScript). Request parameters stay snake\_case in both SDKs.

## Migrating from v1

The v2 SDKs are a deliberate consolidation. A few common v1 → v2 method swaps:

| v1                                           | v2                                             |
| -------------------------------------------- | ---------------------------------------------- |
| `client.upload.knowledge(...)`               | `client.context.ingest(type="knowledge", ...)` |
| `client.upload.addMemory(...)`               | `client.context.ingest(type="memory", ...)`    |
| `client.upload.verifyProcessing(...)`        | `client.context.status(...)`                   |
| `client.search.fullRecall(...)`              | `client.query(type="knowledge", ...)`          |
| `client.search.recallPreferences(...)`       | `client.query(type="memory", ...)`             |
| `client.search.booleanRecall(...)`           | `client.query(query_by="text", ...)`           |
| `client.fetch.listData(...)`                 | `client.context.list(...)`                     |
| `client.fetch.graphRelationsBySourceId(...)` | `client.context.relations(...)`                |
| `client.fetch.content(...)`                  | `client.context.inspect(...)`                  |
| `client.data.delete(...)`                    | `client.context.delete(type="knowledge", ...)` |
| `client.upload.deleteMemory(...)`            | `client.context.delete(type="memory", ...)`    |
| `client.tenant.create(...)`                  | `client.databases.create(...)`                 |
| `client.tenant.getTenantIds(...)`            | `client.databases.list(...)`                   |
| `client.tenant.deleteTenant(...)`            | `client.databases.delete(...)`                 |
| `client.tenant.getInfraStatus(...)`          | `client.databases.status(...)`                 |
| `client.tenant.getSubTenantIds(...)`         | `client.databases.collections(...)`            |
| `client.tenant.monitor(...)`                 | `client.databases.stats(...)`                  |

The v1 SDK methods remain available on the `<2.0.0` releases of `hydradb-sdk` / `@hydradb/sdk` for as long as v1 routes are supported.

## Getting started

### Create a database

A database is an isolated workspace. Most organizations create one database total, with collections for users or teams. See [Multi-Tenant](/essentials/v2/multi-tenant) for the full model.

<CodeGroup>
  ```python Python SDK theme={"dark"}
  response = client.databases.create(
      database="my_first_database",
      database_metadata_schema=[
          {"name": "department", "data_type": "VARCHAR", "enable_match": True},
      ],
  )
  ```

  ```python Python (Async) theme={"dark"}
  response = await async_client.databases.create(database="my_first_database")
  ```

  ```typescript TypeScript SDK theme={"dark"}
  const response = await client.databases.create({
    database: "my_first_database",
    databaseMetadataSchema: [
      { name: "department", dataType: "VARCHAR", enableMatch: true },
    ],
  });
  ```
</CodeGroup>

Database creation is asynchronous. Poll [`databases.status`](/api-reference/v2/endpoint/tenant-status) until provisioning completes:

<CodeGroup>
  ```python Python SDK theme={"dark"}
  import time

  while True:
      status = client.databases.status(database="my_first_database")
      infra = status.data.infra
      if (
          infra.scheduler_status
          and infra.graph_status
          and infra.vectorstore_status.knowledge
          and infra.vectorstore_status.memories
      ):
          break
      time.sleep(2)
  ```

  ```typescript TypeScript SDK theme={"dark"}
  while (true) {
    const status = await client.databases.status({
      database: "my_first_database",
    });
    const { schedulerStatus, graphStatus, vectorstoreStatus } = status.data.infra;
    if (
      schedulerStatus &&
      graphStatus &&
      vectorstoreStatus.knowledge &&
      vectorstoreStatus.memories
    ) break;
    await new Promise((r) => setTimeout(r, 2000));
  }
  ```
</CodeGroup>

### Ingest knowledge

Upload documents, app sources, or both in one call:

<CodeGroup>
  ```python Python SDK theme={"dark"}
  import json

  with open("contract.pdf", "rb") as f1, open("policy.pdf", "rb") as f2:
      result = client.context.ingest(
          type="knowledge",
          database="my_first_database",
          documents=[
              ("contract.pdf", f1, "application/pdf"),
              ("policy.pdf", f2, "application/pdf"),
          ],
          document_metadata=json.dumps([
              {
                  "id": "contract_q4",
                  "metadata": {"department": "legal"},
                  "additional_metadata": {"author": "Alice"},
              },
              {
                  "id": "policy_2025",
                  "metadata": {"department": "legal"},
                  "additional_metadata": {"author": "Bob"},
              },
          ]),
      )
  ```

  ```typescript TypeScript SDK theme={"dark"}
  const result = await client.context.ingest({
    type: "knowledge",
    database: "my_first_database",
    documents: [
      { path: "contract.pdf", filename: "contract.pdf", contentType: "application/pdf" },
      { path: "policy.pdf", filename: "policy.pdf", contentType: "application/pdf" },
    ],
    documentMetadata: JSON.stringify([
      {
        id: "contract_q4",
        metadata: { department: "legal" },
        additional_metadata: { author: "Alice" },
      },
      {
        id: "policy_2025",
        metadata: { department: "legal" },
        additional_metadata: { author: "Bob" },
      },
    ]),
  });
  ```
</CodeGroup>

For app sources (Slack, Notion, Gmail, webpages), pass `app_knowledge` instead of `documents`. You can combine both in a single request.

### Add user memories

Use the same `ingest` method with `type: "memory"`:

<CodeGroup>
  ```python Python SDK theme={"dark"}
  import json

  result = client.context.ingest(
      type="memory",
      database="my_first_database",
      collection="user_alex",
      memories=json.dumps([
          {
              "text": "User prefers detailed technical explanations and dark mode",
              "infer": True,
              "user_name": "Alex",
              "metadata": {"team": "engineering"},
              "additional_metadata": {"source": "onboarding"},
          }
      ]),
  )
  ```

  ```typescript TypeScript SDK theme={"dark"}
  const result = await client.context.ingest({
    type: "memory",
    database: "my_first_database",
    collection: "user_alex",
    memories: JSON.stringify([
      {
        text: "User prefers detailed technical explanations and dark mode",
        infer: true,
        user_name: "Alex",
        metadata: { team: "engineering" },
        additional_metadata: { source: "onboarding" },
      },
    ]),
  });
  ```
</CodeGroup>

<Info>
  **`infer` defaults to `false`.** Set `infer: true` for conversational content where you want HydraDB to extract implicit preferences. Use `infer: false` for content that should be stored verbatim.
</Info>

### Verify processing

Poll `context.status` with the IDs from the ingest response:

<CodeGroup>
  ```python Python SDK theme={"dark"}
  status = client.context.status(
      database="my_first_database",
      ids=["contract_q4", "policy_2025"],
  )
  ```

  ```typescript TypeScript SDK theme={"dark"}
  const status = await client.context.status({
    database: "my_first_database",
    ids: ["contract_q4", "policy_2025"],
  });
  ```
</CodeGroup>

Wait until each `indexing_status` is `completed` (or `graph_creation` if you don't need full graph traversal). See [Context Overview](/api-reference/v2/endpoint/sources-overview) for the full status pipeline.

### Query

A single method covers every retrieval pattern  -  switch `type` and `query_by` to control behavior:

<CodeGroup>
  ```python Python SDK theme={"dark"}
  # Knowledge over documents
  knowledge = client.query(
      database="my_first_database",
      query="What are the pricing tiers?",
      type="knowledge",
      query_by="hybrid",
      mode="thinking",
      graph_context=True,
  )

  # Personalized — searches knowledge AND user memories together
  personalized = client.query(
      database="my_first_database",
      collection="user_alex",
      query="What are the pricing tiers, and how should I explain them to this user?",
      type="all",
      query_by="hybrid",
      mode="thinking",
  )

  # Exact-phrase BM25 search
  exact = client.query(
      database="my_first_database",
      query="ERROR_429 rate limit",
      type="knowledge",
      query_by="text",
      operator="phrase",
  )
  ```

  ```typescript TypeScript SDK theme={"dark"}
  // Knowledge over documents
  const knowledge = await client.query({
    database: "my_first_database",
    query: "What are the pricing tiers?",
    type: "knowledge",
    queryBy: "hybrid",
    mode: "thinking",
    graphContext: true,
  });

  // Personalized — searches knowledge AND user memories together
  const personalized = await client.query({
    database: "my_first_database",
    collection: "user_alex",
    query: "What are the pricing tiers, and how should I explain them to this user?",
    type: "all",
    queryBy: "hybrid",
    mode: "thinking",
  });

  // Exact-phrase BM25 search
  const exact = await client.query({
    database: "my_first_database",
    query: "ERROR_429 rate limit",
    type: "knowledge",
    queryBy: "text",
    operator: "phrase",
  });
  ```
</CodeGroup>

For the full parameter reference, see [Query – Overview](/api-reference/v2/endpoint/query-overview).

### Browse, fetch, and inspect

<CodeGroup>
  ```python Python SDK theme={"dark"}
  # List knowledge sources with pagination
  sources = client.context.list(
      database="my_first_database",
      type="knowledge",
      page=1,
      page_size=50,
  )

  # Inspect graph relationships for a specific source
  relations = client.context.relations(
      database="my_first_database",
      id="doc_12345",
  )

  # Inspect original source content or get a presigned URL
  file = client.context.inspect(
      database="my_first_database",
      id="doc_12345",
      mode="url",
  )

  # Delete a knowledge source
  client.context.delete(
      type="knowledge",
      database="my_first_database",
      ids=["doc_12345"],
  )
  ```

  ```typescript TypeScript SDK theme={"dark"}
  // List knowledge sources with pagination
  const sources = await client.context.list({
    database: "my_first_database",
    type: "knowledge",
    page: 1,
    pageSize: 50,
  });

  // Inspect graph relationships for a specific source
  const relations = await client.context.relations({
    database: "my_first_database",
    id: "doc_12345",
  });

  // Inspect original source content or get a presigned URL
  const file = await client.context.inspect({
    database: "my_first_database",
    id: "doc_12345",
    mode: "url",
  });

  // Delete a knowledge source
  await client.context.delete({
    type: "knowledge",
    database: "my_first_database",
    ids: ["doc_12345"],
  });
  ```
</CodeGroup>

## Response envelope

All responses are wrapped in a consistent envelope:

```json theme={"dark"}
{
  "success": true,
  "data": {
    "message": "Success"
  },
  "error": null,
  "meta": {
    "request_id": "<uuid>",
    "latency_ms": 12.3
  }
}
```

The SDKs return the full envelope. Read the payload from the `data` field (e.g. `response.data`); on failure the SDK raises a typed exception instead of returning an envelope with `error` populated.

## Type safety

<Check>
  Both SDKs are fully typed:

  * **Autocomplete** for all method names and parameters
  * **Type checking** for request and response objects
  * **Inline documentation** for each parameter, sourced from the OpenAPI spec
  * **Compile-time validation** for required vs optional fields
  * **Enum-typed values** for `type`, `query_by`, `operator` and `mode`
</Check>

The SDKs provide exact type parity with the API specification:

* **Request parameters**  -  every field documented in the API reference is reflected in method signatures
* **Response objects**  -  return types match the JSON schema for each endpoint
* **Error types**  -  exception structures mirror error response formats
* **Nested objects**  -  complex parameters and responses keep their full structure

## Error handling

Both SDKs throw exceptions for non-2xx responses. Error objects expose the envelope's `error` payload:

<CodeGroup>
  ```python Python SDK theme={"dark"}
  from hydra_db.errors import NotFoundError, TooManyRequestsError

  try:
      result = client.query(
          database="my_first_database",
          query="...",
          type="knowledge",
      )
  except NotFoundError:
      # Handle missing database / resource (HTTP 404)
      pass
  except TooManyRequestsError:
      # Rate limit (HTTP 429) — retry with backoff
      pass
  ```

  <Info>
    The Python SDK raises one of the typed exceptions exported from `hydra_db.errors`  -  `BadRequestError` (400), `UnauthorizedError` (401), `ForbiddenError` (403), `NotFoundError` (404), `UnprocessableEntityError` (422), `TooManyRequestsError` (429), `InternalServerError` (500), and `ServiceUnavailableError` (503). Each carries `status_code` and the parsed response `body` (from which you can read `body["error"]["code"]`).
  </Info>

  ```typescript TypeScript SDK theme={"dark"}
  import { HydraDBError } from "@hydradb/sdk";

  try {
    const result = await client.query({
      database: "my_first_database",
      query: "...",
      type: "knowledge",
    });
  } catch (error) {
    if (error instanceof HydraDBError) {
      const code = error.body?.error?.code;

      if (code === "DATABASE_NOT_FOUND") {
        // Handle missing database
      } else if (error.statusCode === 429) {
        // Rate limit — retry with backoff
      } else {
        throw error;
      }
    } else {
      throw error;
    }
  }
  ```
</CodeGroup>

For the full list of error codes and retry patterns, see [Error Responses](/api-reference/v2/error-responses).

## IDE-driven discovery

Whether you're using TypeScript, Python, VS Code, PyCharm, or any modern IDE, the workflow is the same:

1. Type the method name → see all available methods
2. Open the parentheses → see all required and optional parameters
3. Press `Cmd+Space` (macOS) or `Ctrl+Space` (Windows/Linux) → get inline documentation

This works because the SDKs are fully typed with comprehensive parameter docs sourced from the OpenAPI spec.

## Related sections

* [API Reference](/api-reference/v2)  -  complete endpoint documentation
* [Error Responses](/api-reference/v2/error-responses)  -  HTTP codes, error codes, retry patterns
* [Quickstart](/get-started/v2/quickstart)  -  build your first integration in five minutes
* [Query](/essentials/v2/query)  -  conceptual overview of `POST /query`
* [Knowledge](/essentials/v2/knowledge) and [Memories](/essentials/v2/memories)  -  ingestion deep-dives
