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

# List Job Changes

> Poll for marketplace job changes since a cursor.

Incremental change feed for the public marketplace: which jobs were published, updated, or closed since a timestamp. Designed for keeping an external job index in sync without re-fetching everything — store the response's `until` and pass it back as the next `since`.

Only the **latest** event per job is returned (a job published then updated in the window appears once, as `UPDATED`). The feed is capped at 5,000 events per call; if you hit the cap, advance `since` to the returned `until` and poll again.

Tokenless, CORS-enabled, and edge-cached the same way as [search](/docs/developers/api-reference/jobs/search). This feed covers public marketplace listings only — for events about *your own* account (proposals, messages, contracts), use [`GET /updates`](/docs/developers/api-reference/updates/poll).

**Requirements:** none — no token, no scope, no feature flag. Rate limited to 120 requests/minute per IP.

## Request

<ParamField query="since" type="string" required>
  ISO 8601 timestamp. Changes at or after this instant are returned. Missing or invalid → `400`.
</ParamField>

## Response

<ResponseField name="since" type="string">
  Echo of the requested window start.
</ResponseField>

<ResponseField name="until" type="string">
  Window end (now). Persist this and pass it as the next call's `since`.
</ResponseField>

<ResponseField name="changes" type="object[]">
  One entry per changed job — the latest event in the window.

  <Expandable title="change entry">
    <ResponseField name="jobId" type="string">
      The job that changed — fetch details with [`GET /jobs/{id}`](/docs/developers/api-reference/jobs/get).
    </ResponseField>

    <ResponseField name="eventType" type="string">
      `PUBLISHED`, `UPDATED`, or `CLOSED`.
    </ResponseField>

    <ResponseField name="occurredAt" type="string">
      ISO timestamp of the event.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="generatedAt" type="string">
  ISO timestamp when the response was generated.
</ResponseField>

## Errors

| Status | `code`         | Meaning                              |
| ------ | -------------- | ------------------------------------ |
| `400`  | `BAD_REQUEST`  | Missing or unparseable `since`       |
| `429`  | `RATE_LIMITED` | Over 120 requests/minute from one IP |

<RequestExample>
  ```bash curl theme={null}
  curl -sS "https://app.opentrain.ai/api/public/v1/jobs/changes?since=2026-06-11T00:00:00Z"
  ```
</RequestExample>

<ResponseExample>
  ```json 200 theme={null}
  {
    "since": "2026-06-11T00:00:00.000Z",
    "until": "2026-06-12T08:00:00.000Z",
    "changes": [
      {
        "jobId": "<JOB_ID>",
        "eventType": "PUBLISHED",
        "occurredAt": "2026-06-11T14:22:05.000Z"
      },
      {
        "jobId": "<OTHER_JOB_ID>",
        "eventType": "CLOSED",
        "occurredAt": "2026-06-12T06:10:41.000Z"
      }
    ],
    "generatedAt": "2026-06-12T08:00:00.000Z"
  }
  ```
</ResponseExample>
