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

# Tables: create, load & query

> Create a table, load rows, query it with SQL, and export it

[Tables](/platform/tables/introduction) store structured rows you can read, write and query with SQL:

* **Create**: `POST /v1/tables`, or `POST /v1/tables/import` from a CSV or Excel file.
* **Rows**: `GET` and `POST` on `/v1/tables/{table_id}/rows`, `POST /v1/tables/{table_id}/rows/bulk`, and `PATCH` and `DELETE` on `/v1/tables/{table_id}/rows/{row_id}`. `DELETE /v1/tables/{table_id}/rows` clears every row.
* **Query**: `POST /v1/tables/query`.
* **Export**: `GET /v1/tables/{table_id}/export`.

Replace `table_id`, `row_id` and `your_api_key`. SQL refers to a table by its identifier (`sql_name`), which the create response includes.

## Create a table

<Note>
  Column `type` is one of `string`, `number`, `boolean`, `datetime`, `file` or
  `reference`. A `reference` column also takes `references` (the linked table's
  `id`) and `on_delete`: `restrict` (the default), `cascade` or `set_null`.
  Every table also gets an `id` primary key: `uuid` (default) or
  `autoincrement`, set with `id_type`. It can't be changed later.
</Note>

<CodeGroup>
  ```python Python SDK theme={null}
  # pip install noxus-sdk
  from noxus_sdk.client import Client

  client = Client(api_key="your_api_key")

  table = client.tables.create(
      name="Customers",
      columns=[
          {"name": "email", "type": "string"},
          {"name": "signups", "type": "number"},
          {"name": "active", "type": "boolean"},
      ],
      id_type="uuid",
  )
  print(table.id, table.sql_name)
  ```

  ```python Python REST theme={null}
  import requests

  resp = requests.post(
      "https://backend.noxus.ai/v1/tables",
      headers={"X-API-KEY": "your_api_key"},
      json={
          "name": "Customers",
          "id_type": "uuid",
          "columns": [
              {"name": "email", "type": "string"},
              {"name": "signups", "type": "number"},
              {"name": "active", "type": "boolean"},
          ],
      },
  )
  table = resp.json()
  print(table["id"], table["sql_name"])
  ```

  ```javascript Node theme={null}
  const resp = await fetch("https://backend.noxus.ai/v1/tables", {
    method: "POST",
    headers: { "X-API-KEY": "your_api_key", "Content-Type": "application/json" },
    body: JSON.stringify({
      name: "Customers",
      id_type: "uuid",
      columns: [
        { name: "email", type: "string" },
        { name: "signups", type: "number" },
        { name: "active", type: "boolean" },
      ],
    }),
  });
  const table = await resp.json();
  console.log(table.id, table.sql_name);
  ```

  ```bash cURL theme={null}
  curl -X POST "https://backend.noxus.ai/v1/tables" \
    -H "X-API-KEY: your_api_key" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Customers",
      "id_type": "uuid",
      "columns": [
        {"name": "email", "type": "string"},
        {"name": "signups", "type": "number"},
        {"name": "active", "type": "boolean"}
      ]
    }'
  ```
</CodeGroup>

## Insert rows in bulk

<Note>
  Each call inserts up to 1,000 rows. Rows are objects keyed by column name.
  For larger loads, send batches, or import a file instead.
</Note>

<CodeGroup>
  ```python Python SDK theme={null}
  from noxus_sdk.client import Client

  client = Client(api_key="your_api_key")
  table = client.tables.get("table_id")

  rows = [{"email": f"user{i}@example.com", "signups": i, "active": True} for i in range(2500)]

  for start in range(0, len(rows), 1000):
      inserted = table.insert_rows(rows[start : start + 1000])
      print(f"inserted {inserted}")
  ```

  ```python Python REST theme={null}
  import requests

  rows = [{"email": f"user{i}@example.com", "signups": i, "active": True} for i in range(2500)]

  for start in range(0, len(rows), 1000):
      resp = requests.post(
          "https://backend.noxus.ai/v1/tables/table_id/rows/bulk",
          headers={"X-API-KEY": "your_api_key"},
          json={"rows": rows[start : start + 1000]},
      )
      print(resp.json())  # {"inserted": 1000}
  ```

  ```javascript Node theme={null}
  const rows = Array.from({ length: 2500 }, (_, i) => ({
    email: `user${i}@example.com`,
    signups: i,
    active: true,
  }));

  for (let start = 0; start < rows.length; start += 1000) {
    const resp = await fetch("https://backend.noxus.ai/v1/tables/table_id/rows/bulk", {
      method: "POST",
      headers: { "X-API-KEY": "your_api_key", "Content-Type": "application/json" },
      body: JSON.stringify({ rows: rows.slice(start, start + 1000) }),
    });
    console.log(await resp.json()); // { inserted: 1000 }
  }
  ```

  ```bash cURL theme={null}
  curl -X POST "https://backend.noxus.ai/v1/tables/table_id/rows/bulk" \
    -H "X-API-KEY: your_api_key" \
    -H "Content-Type: application/json" \
    -d '{"rows": [
      {"email": "ana@example.com", "signups": 3, "active": true},
      {"email": "ben@example.com", "signups": 7, "active": false}
    ]}'
  ```
</CodeGroup>

## Import a CSV or Excel file

<Note>
  Creates a new table from a `.csv` or `.xlsx` file of up to 25 MB and 500,000
  rows. Send the file and a JSON `spec` as multipart form fields. In `columns`,
  `index` is the column's position in the file. Imports are all-or-nothing.
  In Python, use `client.tables.import_csv`. Over REST, don't set
  `Content-Type` yourself; let your HTTP client set the multipart boundary.
</Note>

<CodeGroup>
  ```python Python SDK theme={null}
  from noxus_sdk.client import Client
  from noxus_sdk.resources.tables import ImportColumn

  client = Client(api_key="your_api_key")

  table = client.tables.import_csv(
      "customers.csv",
      name="Customers",
      columns=[
          ImportColumn(index=0, name="email", type="string"),
          ImportColumn(index=1, name="signups", type="number"),
      ],
  )
  print(table.id, table.sql_name)
  ```

  ```python Python REST theme={null}
  import json
  import requests

  spec = {
      "name": "Customers",
      "has_header": True,
      "delimiter": ",",
      "columns": [
          {"index": 0, "name": "email", "type": "string"},
          {"index": 1, "name": "signups", "type": "number"},
      ],
  }
  resp = requests.post(
      "https://backend.noxus.ai/v1/tables/import",
      headers={"X-API-KEY": "your_api_key"},  # no Content-Type: requests sets multipart
      files={"file": open("customers.csv", "rb")},
      data={"spec": json.dumps(spec)},
  )
  print(resp.json())
  ```

  ```javascript Node theme={null}
  import fs from "fs";

  const spec = {
    name: "Customers",
    has_header: true,
    columns: [
      { index: 0, name: "email", type: "string" },
      { index: 1, name: "signups", type: "number" },
    ],
  };

  const form = new FormData();
  form.append("file", new Blob([fs.readFileSync("customers.csv")]), "customers.csv");
  form.append("spec", JSON.stringify(spec));

  const resp = await fetch("https://backend.noxus.ai/v1/tables/import", {
    method: "POST",
    headers: { "X-API-KEY": "your_api_key" }, // fetch sets the multipart boundary
    body: form,
  });
  console.log(await resp.json());
  ```

  ```bash cURL theme={null}
  curl -X POST "https://backend.noxus.ai/v1/tables/import" \
    -H "X-API-KEY: your_api_key" \
    -F "file=@customers.csv" \
    -F 'spec={"name": "Customers", "has_header": true, "columns": [{"index": 0, "name": "email", "type": "string"}, {"index": 1, "name": "signups", "type": "number"}]}'
  ```
</CodeGroup>

## Query with SQL

<Note>
  Runs one read-only `SELECT` across the workspace's tables, including
  [platform tables](/platform/tables/platform-tables). Results are capped at
  10,000 rows and queries time out after 10 seconds, so add `LIMIT` and
  `ORDER BY`. See [SQL Queries](/platform/tables/using/sql) for what's allowed.
</Note>

<CodeGroup>
  ```python Python SDK theme={null}
  from noxus_sdk.client import Client

  client = Client(api_key="your_api_key")

  result = client.tables.query(
      "SELECT email, signups FROM customers WHERE active ORDER BY signups DESC LIMIT 10"
  )
  print(result.columns)
  for row in result.rows:
      print(row)
  ```

  ```python Python REST theme={null}
  import requests

  resp = requests.post(
      "https://backend.noxus.ai/v1/tables/query",
      headers={"X-API-KEY": "your_api_key"},
      json={"sql": "SELECT email, signups FROM customers WHERE active ORDER BY signups DESC LIMIT 10"},
  )
  result = resp.json()
  for row in result["rows"]:
      print(row)
  ```

  ```javascript Node theme={null}
  const resp = await fetch("https://backend.noxus.ai/v1/tables/query", {
    method: "POST",
    headers: { "X-API-KEY": "your_api_key", "Content-Type": "application/json" },
    body: JSON.stringify({
      sql: "SELECT email, signups FROM customers WHERE active ORDER BY signups DESC LIMIT 10",
    }),
  });
  const { columns, rows } = await resp.json();
  console.log(columns, rows);
  ```

  ```bash cURL theme={null}
  curl -X POST "https://backend.noxus.ai/v1/tables/query" \
    -H "X-API-KEY: your_api_key" \
    -H "Content-Type: application/json" \
    -d '{"sql": "SELECT email, signups FROM customers WHERE active ORDER BY signups DESC LIMIT 10"}'
  ```
</CodeGroup>

## Page through rows

<Note>
  Returns up to 500 rows per page, with `limit` and `offset`. The response also
  carries `total`, the number of matching rows. `search` matches text in any
  column except `id`. The SDK's `iter_rows()` pages for you.
</Note>

<CodeGroup>
  ```python Python SDK theme={null}
  from noxus_sdk.client import Client

  client = Client(api_key="your_api_key")
  table = client.tables.get("table_id")

  for row in table.iter_rows(page_size=500):
      print(row)
  ```

  ```python Python REST theme={null}
  import requests

  offset = 0
  while True:
      resp = requests.get(
          "https://backend.noxus.ai/v1/tables/table_id/rows",
          headers={"X-API-KEY": "your_api_key"},
          params={"limit": 500, "offset": offset},
      )
      page = resp.json()
      for row in page["rows"]:
          print(row)
      offset += len(page["rows"])
      if offset >= page["total"]:
          break
  ```

  ```javascript Node theme={null}
  let offset = 0;
  while (true) {
    const params = new URLSearchParams({ limit: "500", offset: String(offset) });
    const resp = await fetch(`https://backend.noxus.ai/v1/tables/table_id/rows?${params}`, {
      headers: { "X-API-KEY": "your_api_key" },
    });
    const page = await resp.json();
    page.rows.forEach((row) => console.log(row));
    offset += page.rows.length;
    if (offset >= page.total) break;
  }
  ```

  ```bash cURL theme={null}
  curl "https://backend.noxus.ai/v1/tables/table_id/rows?limit=500&offset=0" \
    -H "X-API-KEY: your_api_key"
  ```
</CodeGroup>

## Update and delete a row

<Note>
  Target a row by its `id`. An update changes only the columns you send and
  returns the updated row. Deleting a row can't be undone.
</Note>

<CodeGroup>
  ```python Python SDK theme={null}
  from noxus_sdk.client import Client

  client = Client(api_key="your_api_key")
  table = client.tables.get("table_id")

  updated = table.update_row("row_id", {"signups": 8, "active": False})
  print(updated)

  table.delete_row("row_id")
  ```

  ```python Python REST theme={null}
  import requests

  base = "https://backend.noxus.ai/v1/tables/table_id/rows/row_id"
  headers = {"X-API-KEY": "your_api_key"}

  resp = requests.patch(base, headers=headers, json={"values": {"signups": 8, "active": False}})
  print(resp.json()["row"])

  requests.delete(base, headers=headers)  # {"success": true}
  ```

  ```javascript Node theme={null}
  const url = "https://backend.noxus.ai/v1/tables/table_id/rows/row_id";

  const updated = await fetch(url, {
    method: "PATCH",
    headers: { "X-API-KEY": "your_api_key", "Content-Type": "application/json" },
    body: JSON.stringify({ values: { signups: 8, active: false } }),
  });
  console.log((await updated.json()).row);

  await fetch(url, { method: "DELETE", headers: { "X-API-KEY": "your_api_key" } });
  ```

  ```bash cURL theme={null}
  # Update
  curl -X PATCH "https://backend.noxus.ai/v1/tables/table_id/rows/row_id" \
    -H "X-API-KEY: your_api_key" \
    -H "Content-Type: application/json" \
    -d '{"values": {"signups": 8, "active": false}}'

  # Delete
  curl -X DELETE "https://backend.noxus.ai/v1/tables/table_id/rows/row_id" \
    -H "X-API-KEY: your_api_key"
  ```
</CodeGroup>

## Export to CSV

<Note>
  Downloads every row as a UTF-8 CSV, with column identifiers in the header and
  `id` first.
</Note>

<CodeGroup>
  ```python Python SDK theme={null}
  from noxus_sdk.client import Client

  client = Client(api_key="your_api_key")
  table = client.tables.get("table_id")

  with open("customers.csv", "wb") as f:
      f.write(table.export_csv())
  ```

  ```python Python REST theme={null}
  import requests

  resp = requests.get(
      "https://backend.noxus.ai/v1/tables/table_id/export",
      headers={"X-API-KEY": "your_api_key"},
  )
  with open("customers.csv", "wb") as f:
      f.write(resp.content)
  ```

  ```javascript Node theme={null}
  import fs from "fs";

  const resp = await fetch("https://backend.noxus.ai/v1/tables/table_id/export", {
    headers: { "X-API-KEY": "your_api_key" },
  });
  fs.writeFileSync("customers.csv", Buffer.from(await resp.arrayBuffer()));
  ```

  ```bash cURL theme={null}
  curl "https://backend.noxus.ai/v1/tables/table_id/export" \
    -H "X-API-KEY: your_api_key" \
    -o customers.csv
  ```
</CodeGroup>

## Async / await with the SDK

<Note>
  Use the `a`-prefixed coroutines (`acreate`, `aget`, `ainsert_rows`, `aquery`,
  `aiter_rows`, `aupdate_row`, `adelete_row`, `aexport_csv`) from async code so
  the event loop stays responsive.
</Note>

```python theme={null}
import asyncio
from noxus_sdk.client import Client


async def main():
    client = Client(api_key="your_api_key")
    table = await client.tables.aget("table_id")

    await table.ainsert_rows([{"email": "ana@example.com", "signups": 3, "active": True}])

    result = await client.tables.aquery("SELECT COUNT(*) AS n FROM customers")
    print(result.rows)

    async for row in table.aiter_rows(page_size=500):
        print(row)


asyncio.run(main())
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.