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

# List what changed on a watch's source

> The sections this watch's source added, amended, or removed, newest first. This is the per-item detail behind the counts in a notification: not "3 sections changed" but which three.

Covers the source's whole captured history, not just the period since you subscribed, so a watch created today can still read back what the source did before it. For the narrower 'what has my alert actually covered' view, pass `since=` the watch's own `createdAt`.

History is bounded by capture, not by the age of the law: events exist only from when change capture began for that source, and are swept at 24 months. An empty list means no captured change, never 'never amended' -- the publisher's own history, where there is one, is on the section's `amendmentHistory`.

**Safe to poll alongside a webhook or email watch.** This endpoint is read-only and there is no server-side 'last checked' state to interfere with: notifications fire when a corpus refresh completes, not by comparing against a watermark, and nothing here writes one. Polling cannot suppress, advance, or double-fire a delivery. (`lastNotifiedAt` on the watch records the outcome of the last delivery attempt for your visibility; it is never an input.)

**Paging.** Pass `sinceId` with `meta.cursor` from your previous page to get only what is new. `meta.hasMore` is true when the page filled to `limit`, so keep calling with the new cursor until it is false. Use `order=asc` while catching up.

`changeKind` is `added` (a new section), `amended` (its content was replaced), or `removed` (it disappeared from a full refresh of the source). `citation` and `title` are null for corpora that do not carry them.

**Free.**



## OpenAPI

````yaml https://api.vaquill.ai/external/openapi.json get /api/v1/watches/{watch_id}/changes
openapi: 3.1.0
info:
  title: Vaquill Developer API
  description: >-
    Public API for legal statutes and legislation.


    **Authentication**: Pass your API key via the `Authorization: Bearer
    vq_key_...` header.


    **Credits**: Each call costs API credits. See `GET
    /api/v1/api-credits/pricing` for the full pricing matrix.


    ## Jurisdictions


    | Jurisdiction | Coverage | `countryCode` |

    |---|---|---|

    | **United States** | USC, CFR, nearly all 50 state statutory codes (plus DC
    and Puerto Rico), state constitutions, and court rules. | `US` |


    See the full [Coverage page](https://www.vaquill.ai/docs/coverage) for the
    jurisdiction-by-jurisdiction breakdown, or call `/us/statutes/coverage` for
    live counts.


    ## Endpoints


    - **US Statutes**: Search and retrieve USC, CFR, state statutes,
    constitutions, court rules, executive orders, and regulations with full text

    - **Board Watches**: Subscribe to a corpus source and get a webhook or email
    when it refreshes with real changes (free, API key required)

    - **Pricing**: Credit costs per endpoint (no auth required)


    ## Rate limits


    Limits are enforced per API key and scale with your plan. A `429` response
    includes a `Retry-After` header.


    | Plan | Per minute | Per hour | Per day |

    |---|---|---|---|

    | Vaquill API Pro | 30 | 500 | 1,000 |

    | Vaquill API Business | 150 | 2,500 | 10,000 |


    For full documentation, visit the [API
    Reference](https://www.vaquill.ai/docs/api-reference/).
  version: 1.0.0
  contact:
    name: Vaquill API Support
    url: https://www.vaquill.ai
    email: support@vaquill.ai
  license:
    name: Proprietary
    url: https://www.vaquill.ai/terms
  termsOfService: https://www.vaquill.ai/terms
servers:
  - url: https://api.vaquill.ai
    description: Production
security:
  - ApiKeyAuth: []
tags:
  - name: US Statutes
    description: >-
      Search and retrieve US statutes. Covers the United States Code (USC), Code
      of Federal Regulations (CFR), all 50 state statutes, constitutions, court
      rules, executive orders, Federal Register agency rules, agency guidance,
      and state regulations. Scope a search with the `corpusType` filter.


      - **Search**: Search across all USC, CFR, state, and regulatory sections

      - **Section**: Get metadata, citation, hierarchy, and source links for a
      specific section

      - **Body**: Get the full text of a section
  - name: Board Watches
    description: >-
      Law-change alerts. Subscribe to a **board** (a tracked corpus source,
      identified by `corpusType` plus an optional `state`) and get a webhook or
      email whenever it refreshes with real changes, naming the exact sections
      that changed. Fetch old-vs-new text for any amended section via the change
      diff.


      Boards refresh on their own existing cadence; watching one does not change
      how often it refreshes. Every endpoint here is **free**: authenticated by
      API key and rate-limited, but not credit-metered.


      See the [Law Change Alerts
      guide](https://www.vaquill.ai/docs/api-guide/alerts) for the full
      walkthrough.
  - name: Pricing
    description: >-
      Credit pricing and conversion rates. No authentication required. 1 credit
      = $0.01 USD.
externalDocs:
  description: Full API Reference
  url: https://www.vaquill.ai/docs/api-reference/
paths:
  /api/v1/watches/{watch_id}/changes:
    get:
      tags:
        - Board Watches
      summary: List what changed on a watch's source
      description: >-
        The sections this watch's source added, amended, or removed, newest
        first. This is the per-item detail behind the counts in a notification:
        not "3 sections changed" but which three.


        Covers the source's whole captured history, not just the period since
        you subscribed, so a watch created today can still read back what the
        source did before it. For the narrower 'what has my alert actually
        covered' view, pass `since=` the watch's own `createdAt`.


        History is bounded by capture, not by the age of the law: events exist
        only from when change capture began for that source, and are swept at 24
        months. An empty list means no captured change, never 'never amended' --
        the publisher's own history, where there is one, is on the section's
        `amendmentHistory`.


        **Safe to poll alongside a webhook or email watch.** This endpoint is
        read-only and there is no server-side 'last checked' state to interfere
        with: notifications fire when a corpus refresh completes, not by
        comparing against a watermark, and nothing here writes one. Polling
        cannot suppress, advance, or double-fire a delivery. (`lastNotifiedAt`
        on the watch records the outcome of the last delivery attempt for your
        visibility; it is never an input.)


        **Paging.** Pass `sinceId` with `meta.cursor` from your previous page to
        get only what is new. `meta.hasMore` is true when the page filled to
        `limit`, so keep calling with the new cursor until it is false. Use
        `order=asc` while catching up.


        `changeKind` is `added` (a new section), `amended` (its content was
        replaced), or `removed` (it disappeared from a full refresh of the
        source). `citation` and `title` are null for corpora that do not carry
        them.


        **Free.**
      operationId: list_watch_changes_api_v1_watches__watch_id__changes_get
      parameters:
        - name: watch_id
          in: path
          required: true
          schema:
            type: string
            title: Watch Id
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            maximum: 200
            minimum: 1
            description: Max changes to return.
            default: 50
            title: Limit
          description: Max changes to return.
        - name: sinceId
          in: query
          required: false
          schema:
            anyOf:
              - type: integer
                minimum: 0
              - type: 'null'
            description: >-
              Return only changes with an `id` greater than this. The cursor to
              build on: ids are a monotonic sequence, so this is exact, immune
              to clock skew, and cannot drop two changes that share a timestamp.
              Carry `meta.cursor` forward from your last page.
            title: Sinceid
          description: >-
            Return only changes with an `id` greater than this. The cursor to
            build on: ids are a monotonic sequence, so this is exact, immune to
            clock skew, and cannot drop two changes that share a timestamp.
            Carry `meta.cursor` forward from your last page.
        - name: beforeId
          in: query
          required: false
          schema:
            anyOf:
              - type: integer
                minimum: 0
              - type: 'null'
            description: >-
              Return only changes with an `id` less than this: the cursor for
              walking BACK through history, where `sinceId` walks forward into
              new changes. A newest-first reader needs this one, since paging
              down a descending list means asking for what sits below the lowest
              id already held. Pass the smallest `id` on your last page.
            title: Beforeid
          description: >-
            Return only changes with an `id` less than this: the cursor for
            walking BACK through history, where `sinceId` walks forward into new
            changes. A newest-first reader needs this one, since paging down a
            descending list means asking for what sits below the lowest id
            already held. Pass the smallest `id` on your last page.
        - name: since
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: 'null'
            description: >-
              Return only changes detected strictly after this ISO-8601
              timestamp. Coarser than `sinceId` (a single refresh writes many
              rows at the same instant), but useful when all you kept was the
              `detectedAt` from a webhook body. Both may be combined.
            examples:
              - '2026-08-07T04:10:00Z'
            title: Since
          description: >-
            Return only changes detected strictly after this ISO-8601 timestamp.
            Coarser than `sinceId` (a single refresh writes many rows at the
            same instant), but useful when all you kept was the `detectedAt`
            from a webhook body. Both may be combined.
        - name: changeKind
          in: query
          required: false
          schema:
            anyOf:
              - type: array
                items:
                  enum:
                    - added
                    - amended
                    - removed
                  type: string
              - type: 'null'
            description: >-
              Filter to these kinds. Repeat the parameter to pass several
              (`?changeKind=added&changeKind=amended`). Omit for all three.
            title: Changekind
          description: >-
            Filter to these kinds. Repeat the parameter to pass several
            (`?changeKind=added&changeKind=amended`). Omit for all three.
        - name: order
          in: query
          required: false
          schema:
            enum:
              - asc
              - desc
            type: string
            description: >-
              `desc` (default) is newest first, for showing a recent-activity
              list. Use `asc` when catching up from a cursor: walking forward
              means a page that hits `limit` leaves the gap at a known end.
            default: desc
            title: Order
          description: >-
            `desc` (default) is newest first, for showing a recent-activity
            list. Use `asc` when catching up from a cursor: walking forward
            means a page that hits `limit` leaves the gap at a known end.
      responses:
        '200':
          description: What changed, newest first.
          content:
            application/json:
              schema: {}
              example:
                data:
                  changes:
                    - id: 91
                      refreshLogId: 4412
                      corpusType: cfr
                      changeKind: amended
                      actId: CFR_T21_P314_S314_50
                      citation: 21 CFR 314.50
                      title: Content and format of an NDA
                      detectedAt: '2026-08-07T04:10:00Z'
                      hasDiff: false
                meta:
                  processingTimeMs: 12
                  creditsConsumed: 0
                  cursor: 91
                  hasMore: false
        '400':
          description: Invalid changeKind, order, or since timestamp.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiDetailError'
        '401':
          description: Invalid or missing API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiDetailError'
        '403':
          description: API key lacks `research:read` scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiDetailError'
        '404':
          description: Watch not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiDetailError'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
        '429':
          description: Rate limit exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiDetailError'
components:
  schemas:
    ApiDetailError:
      properties:
        detail:
          type: string
          title: Detail
          description: >-
            Human-readable reason, safe to surface to an end user. Branch on the
            HTTP status rather than on this string: the wording is not part of
            the contract and may be reworded, but 401 (bad key), 402 (out of
            credits), 403 (missing scope), 404 (no such resource) and 429 (rate
            limited) are stable.
          examples:
            - Insufficient API credits.
      type: object
      required:
        - detail
      title: ApiDetailError
      description: |-
        Error envelope the API actually returns.

        All errors (401/402/403/404/422/429/5xx) come back as a single
        `detail` string (FastAPI default), e.g. `{"detail": "Insufficient API
        credits."}`. Documenting the real shape so client code can rely on it.
    HTTPValidationError:
      properties:
        detail:
          items:
            $ref: '#/components/schemas/ValidationError'
          type: array
          title: Detail
      type: object
      title: HTTPValidationError
    ValidationError:
      properties:
        loc:
          items:
            anyOf:
              - type: string
              - type: integer
          type: array
          title: Location
        msg:
          type: string
          title: Message
        type:
          type: string
          title: Error Type
      type: object
      required:
        - loc
        - msg
        - type
      title: ValidationError
  securitySchemes:
    ApiKeyAuth:
      type: http
      scheme: bearer
      bearerFormat: vq_key_*
      description: >-
        API key issued from the developer dashboard. Pass as `Authorization:
        Bearer vq_key_...`

````