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

# Bulk export a full table with Raw endpoints

> Use /raw/{table_name} to export all rows from a source table for warehousing or analytics using cursor-based pagination and consistent snapshots.

Export an entire source table from the Trassets Data API into your warehouse or analytics pipeline. Raw endpoints return unfiltered rows from the underlying database with cursor-based pagination and snapshot consistency headers.

<Steps>
  <Step title="List available tables">
    Call `GET /raw/tables` to discover which tables are available, their primary keys, row counts, and sync status.

    ```bash theme={null}
    curl -s "https://api.trassets.ai/raw/tables" \
      -H "X-API-Key: $TRASSETS_API_KEY"
    ```

    Example response:

    ```json theme={null}
    [
      {
        "table_name": "ixhaus_contracts",
        "primary_key": "contract_id",
        "status": "synced",
        "row_count": 12500,
        "last_sync_at": "2026-04-10T08:00:00"
      }
    ]
    ```
  </Step>

  <Step title="Start the export">
    Request the first page of the table you want. Set `limit` up to the maximum of `10000`.

    ```bash theme={null}
    curl -s "https://api.trassets.ai/raw/ixhaus_contracts?limit=10000" \
      -H "X-API-Key: $TRASSETS_API_KEY"
    ```

    Example response:

    ```json theme={null}
    {
      "data": [
        { "contract_id": 1, "tenant": "Müller GmbH" }
      ],
      "count": 1,
      "next_cursor": "eyJjb2wiOiAiY29udHJhY3RfaWQiLCAidmFsIjogMX0=",
      "table_name": "ixhaus_contracts",
      "total_rows": 12500
    }
    ```
  </Step>

  <Step title="Paginate with the cursor">
    Pass the `next_cursor` value into the `cursor` query parameter for each subsequent request. Repeat until `next_cursor` is `null`.

    ```bash theme={null}
    #!/bin/bash
    TABLE="ixhaus_contracts"
    CURSOR=""

    while true; do
      if [ -z "$CURSOR" ]; then
        RESP=$(curl -s "https://api.trassets.ai/raw/${TABLE}?limit=10000" \
          -H "X-API-Key: $TRASSETS_API_KEY")
      else
        RESP=$(curl -s "https://api.trassets.ai/raw/${TABLE}?limit=10000&cursor=${CURSOR}" \
          -H "X-API-Key: $TRASSETS_API_KEY")
      fi

      echo "$RESP" | jq '.data[]'

      CURSOR=$(echo "$RESP" | jq -r '.next_cursor')
      if [ "$CURSOR" = "null" ] || [ -z "$CURSOR" ]; then
        break
      fi
    done
    ```
  </Step>

  <Step title="Verify snapshot consistency">
    Check the `X-Snapshot-At` response header on every page. All rows within a single cursor sequence are consistent as of that timestamp. If the header changes between pages, start the export over to ensure a single snapshot view.

    ```bash theme={null}
    curl -sI "https://api.trassets.ai/raw/ixhaus_contracts?limit=10000" \
      -H "X-API-Key: $TRASSETS_API_KEY" | grep -i x-snapshot-at
    ```
  </Step>

  <Step title="Full Python example">
    A complete script that exports a table page by page, following the cursor until
    exhausted, and verifies snapshot consistency across pages. Requires the
    [`requests`](https://pypi.org/project/requests/) package.

    ```python bulk_export.py theme={null}
    import os
    import requests

    API_KEY = os.environ["TRASSETS_API_KEY"]
    BASE_URL = "https://api.trassets.ai"
    HEADERS = {"X-API-Key": API_KEY}


    def export_table(table_name: str, page_size: int = 10000) -> list[dict]:
        """Export every row of a raw table, verifying a single consistent snapshot."""
        rows: list[dict] = []
        cursor = None
        snapshot_at = None

        while True:
            params = {"limit": page_size}
            if cursor:
                params["cursor"] = cursor

            resp = requests.get(
                f"{BASE_URL}/raw/{table_name}", headers=HEADERS, params=params, timeout=60
            )
            resp.raise_for_status()

            page_snapshot = resp.headers.get("X-Snapshot-At")
            if snapshot_at is None:
                snapshot_at = page_snapshot
            elif page_snapshot != snapshot_at:
                raise RuntimeError(
                    f"Snapshot changed mid-export ({snapshot_at} -> {page_snapshot}); "
                    "restart the export from the beginning."
                )

            body = resp.json()
            rows.extend(body["data"])
            cursor = body["next_cursor"]
            if cursor is None:
                break

        return rows, snapshot_at


    if __name__ == "__main__":
        rows, snapshot_at = export_table("ixhaus_contracts")
        print(f"Exported {len(rows)} rows as of snapshot {snapshot_at}")
    ```
  </Step>
</Steps>

<Note>
  Cursor sequences remain stable under concurrent writes. Because raw tables can receive updates during a long export, `X-Snapshot-At` tells you which sync version each page belongs to. For a guaranteed single-snapshot export, retry from the beginning if the snapshot timestamp differs across pages.
</Note>
