> ## 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.

# Use delta sync to detect changed tables efficiently

> Poll /changes with the since parameter and ETags to avoid unnecessary fetches and discover which tables have new data since your last sync.

Discover which tables have been updated since your last sync without fetching every endpoint. The Trassets Data API `GET /changes` endpoint returns metadata for changed tables and supports conditional requests via ETag to skip polling when nothing is new.

<Tip>
  Poll `/changes` every 15–60 minutes. The underlying sync runs as a daily batch, so polling more often will not surface newer data — it only spends more of your 500 req/h budget. Combine this with the `since`/ETag pattern below so unchanged polls stay cheap.
</Tip>

<Steps>
  <Step title="Poll for changed tables">
    Send a `GET` request to `/changes` with an ISO-8601 UTC `since` timestamp. The response lists every table whose `last_sync_at` is after that timestamp, including the corresponding endpoint URL.

    ```bash theme={null}
    curl -s "https://api.trassets.ai/changes?since=2026-07-01T00:00:00Z" \
      -H "X-API-Key: $TRASSETS_API_KEY"
    ```

    Example response:

    ```json theme={null}
    [
      {
        "table_name": "properties",
        "last_sync_at": "2026-07-07T06:00:00",
        "category": "property",
        "api_table_name": "properties",
        "endpoint_url": "/property/properties"
      },
      {
        "table_name": "ixhaus_contracts",
        "last_sync_at": "2026-07-07T06:05:00",
        "endpoint_url": "/raw/ixhaus_contracts"
      }
    ]
    ```
  </Step>

  <Step title="Fetch only changed tables">
    For each item returned by `/changes`, request the corresponding endpoint only. Skip any table that is absent from the response. This avoids unnecessary data transfer and preserves your 500 req/h rate limit budget.
  </Step>

  <Step title="Use conditional GET on subsequent polls">
    Store the `ETag` header from the first successful `/changes` response. On your next poll, send it back as `If-None-Match`.

    ```bash theme={null}
    curl -s "https://api.trassets.ai/changes?since=2026-07-01T00:00:00Z" \
      -H "X-API-Key: $TRASSETS_API_KEY" \
      -H "If-None-Match: \"abc123\""
    ```

    If no tables have changed since the ETag was issued, the API returns `304 Not Modified` with an empty body and does not count the request against the change-data query cost.
  </Step>

  <Step title="Advance the since parameter">
    After a successful sync, store the current timestamp and use it as the `since` value for the next poll. This creates a sliding window so each run evaluates only the interval since the previous run.
  </Step>

  <Step title="Full Python example">
    A complete poller that tracks the `since` timestamp and `ETag` between runs, and only
    processes tables that actually changed. Requires the
    [`requests`](https://pypi.org/project/requests/) package.

    ```python poll_changes.py theme={null}
    import os
    from datetime import datetime, timezone

    import requests

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


    def poll_changes(since: str, etag: str | None = None):
        """Poll /changes and return (changed_tables, new_etag, new_since).

        changed_tables is an empty list on a 304 Not Modified response.
        """
        headers = dict(HEADERS)
        if etag:
            headers["If-None-Match"] = etag

        resp = requests.get(
            f"{BASE_URL}/changes", headers=headers, params={"since": since}, timeout=30
        )

        now = datetime.now(timezone.utc).isoformat()
        if resp.status_code == 304:
            return [], etag, now

        resp.raise_for_status()
        return resp.json(), resp.headers.get("ETag"), now


    if __name__ == "__main__":
        since = "2026-07-01T00:00:00Z"
        etag = None

        changed, etag, since = poll_changes(since, etag)
        for table in changed:
            print(f"Changed: {table['table_name']} -> {table['endpoint_url']}")
        if not changed:
            print("No changes since last poll.")

        # Persist `since` and `etag` and pass them into the next scheduled run.
    ```
  </Step>
</Steps>

<Warning>
  The `since` parameter must not be older than 30 days. Requests with a timestamp beyond this lookback window return an error. Run a full table refresh if your last successful sync was more than 30 days ago.
</Warning>
