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

> ## Agent Instructions
> Setting up Context.dev for a user? Follow https://docs.context.dev/agent-quickstart.md. MCP clients sign in with OAuth and need no API key; application code reads CONTEXT_DEV_API_KEY.

# Monitor targets

> Configure page, sitemap, and extract targets, choose how changes are detected, and update a target without losing settings.

`target` says what a monitor watches, and `change_detection` says how a difference becomes a change. Only `name` and `target` are required.

<CodeGroup>
  ```typescript TypeScript theme={null}
  import ContextDev from "context.dev";

  const client = new ContextDev({ apiKey: process.env.CONTEXT_DEV_API_KEY });

  const monitor = await client.monitors.create({
    name: "Terms of service",
    target: { type: "page", url: "https://example.com/legal/terms" },
  });
  console.log(monitor.id, monitor.change_detection.type);
  ```

  ```python Python theme={null}
  import os
  from context.dev import ContextDev

  client = ContextDev(api_key=os.environ["CONTEXT_DEV_API_KEY"])

  monitor = client.monitors.create(
      name="Terms of service",
      target={"type": "page", "url": "https://example.com/legal/terms"},
  )
  print(monitor.id, monitor.change_detection.type)
  ```

  ```ruby Ruby theme={null}
  require "cgi/core"
  require "context_dev"

  client = ContextDev::Client.new(api_key: ENV.fetch("CONTEXT_DEV_API_KEY"))

  monitor = client.monitors.create(
    name: "Terms of service",
    target: {type: "page", url: "https://example.com/legal/terms"},
  )
  puts monitor.id, monitor.change_detection.type
  ```

  ```go Go theme={null}
  package main

  import (
  	"context"
  	"fmt"
  	"os"

  	contextdev "github.com/context-dot-dev/context-go-sdk/v2"
  	"github.com/context-dot-dev/context-go-sdk/v2/option"
  )

  func main() {
  	client := contextdev.NewClient(option.WithAPIKey(os.Getenv("CONTEXT_DEV_API_KEY")))

  	monitor, err := client.Monitors.New(context.Background(), contextdev.MonitorNewParams{
  		Name: "Terms of service",
  		Target: contextdev.MonitorNewParamsTargetUnion{
  			OfPage: &contextdev.MonitorNewParamsTargetPage{
  				Type: "page",
  				URL:  "https://example.com/legal/terms",
  			},
  		},
  	})
  	if err != nil {
  		panic(err)
  	}
  	fmt.Println(monitor.ID, monitor.ChangeDetection.Type)
  }
  ```

  ```php PHP theme={null}
  <?php
  require __DIR__.'/vendor/autoload.php';

  use ContextDev\Client;

  $client = new Client(apiKey: getenv('CONTEXT_DEV_API_KEY'));

  $monitor = $client->monitors->create(
      name: "Terms of service",
      target: ["type" => "page", "url" => "https://example.com/legal/terms"],
  );
  echo $monitor->id, " ", $monitor->changeDetection->type, PHP_EOL;
  ```

  ```bash cURL theme={null}
  curl https://api.context.dev/v1/monitors \
    -H "Authorization: Bearer $CONTEXT_DEV_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Terms of service",
      "target": { "type": "page", "url": "https://example.com/legal/terms" }
    }'
  ```
</CodeGroup>

This page target has no `instructions`, so detection is inferred as `exact`, and the monitor runs daily.

## Change detection

| Target                        | Detection  |
| ----------------------------- | ---------- |
| `page` without `instructions` | `exact`    |
| `page` with `instructions`    | `semantic` |
| `sitemap`                     | `exact`    |
| `extract`                     | `semantic` |

Omit `change_detection` and it's inferred as shown. Any other pair returns `400`, including `exact` with page `instructions` and `semantic` without them. Semantic detection takes an optional `confidence_threshold` from 0 to 1 (default `0.75`). The judge rates each confirmed difference:

* It matters, with confidence at or above the threshold: reported as a change, and the baseline moves to the new content.
* It matters, with confidence below the threshold: not reported yet. The baseline stays, so the next run checks the same difference again.
* It doesn't matter: not reported, and the baseline takes the new content silently.

## Page

Watch the visible text of one page.

| Field                  | Default      | Notes                                                                                                                           |
| ---------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------- |
| `url`                  | Required     | Public `http(s)` URL.                                                                                                           |
| `include_selectors`    | Main content | CSS selectors for the regions to watch, combined in document order. Omit it or send `[]` for automatic main-content extraction. |
| `exclude_selectors`    | None         | CSS selectors to remove before comparing. They win over `include_selectors`.                                                    |
| `instructions`         | None         | 1–2,000 characters on what matters and what to ignore. Makes detection `semantic`.                                              |
| `normalize_whitespace` | `true`       | Ignore whitespace-only differences. Set `false` when spacing matters.                                                           |

Each selector list takes up to 50 selectors of 1–2,048 characters. Every run fetches the page fresh. If the filtered page has no usable text, the run fails with `fetch_failed` and the old baseline stays. An exact monitor reports the first fetch that differs from the baseline. A semantic monitor fetches the page again to confirm a difference before the judge sees it.

```json theme={null}
{
  "name": "Status page incidents",
  "target": {
    "type": "page",
    "url": "https://status.example.com",
    "instructions": "Report new or resolved incidents. Ignore uptime percentages.",
    "include_selectors": ["#incidents"]
  },
  "change_detection": { "type": "semantic", "confidence_threshold": 0.8 }
}
```

## Sitemap

Watch a sitemap for URLs that are added or removed.

| Field      | Default  | Notes                                                                                                                         |
| ---------- | -------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `url`      | Required | A URL ending in `.xml` or `.xml.gz` is read as that sitemap file. Any other URL reads the domain's sitemaps.                  |
| `include`  | None     | Up to 50 path patterns of 1–200 characters, such as `/blog/*`. A pattern must match the whole path, and `*` matches anything. |
| `exclude`  | None     | Path patterns to leave out, in the same format.                                                                               |
| `max_urls` | `5000`   | 1–10,000. The cap applies to URLs as read, before `include` and `exclude`.                                                    |

URLs are normalized (lowercase host, no trailing slash or fragment) and kept only for the site and its subdomains. When a run sees a difference, it reads the sitemap again and reports only the URLs both reads agree on.

```json theme={null}
{
  "name": "New blog posts",
  "target": {
    "type": "sitemap",
    "url": "https://example.com/sitemap.xml",
    "include": ["/blog/*"],
    "exclude": ["/blog/tag/*"]
  }
}
```

## Extract

Watch the pages of a site that matter for your `instructions`.

| Field               | Default                | Notes                                                                             |
| ------------------- | ---------------------- | --------------------------------------------------------------------------------- |
| `url`               | Required               | Root URL to start from.                                                           |
| `instructions`      | Required               | 1–2,000 characters on which pages and facts to track and which changes to report. |
| `schema`            | Summary and key points | Optional JSON Schema, up to 20,000 characters serialized and 12 levels deep.      |
| `max_pages`         | `10`                   | 1–50 pages to track.                                                              |
| `max_depth`         | None                   | 0–10 link levels from `url`. `0` tracks only `url`.                               |
| `follow_subdomains` | `false`                | Also consider pages on subdomains.                                                |

A discovery crawl picks up to `max_pages` pages that fit `instructions` and `schema`, and it refreshes that set periodically. Each run fetches the tracked pages again, confirms text differences, and asks the judge whether they matter. Extract monitors never compare extracted data. `schema` gives the judge extra context and shapes the baseline `data` on [Retrieve a monitor](/api-reference/monitors/retrieve), which refreshes about once a day. Changes contain diffs, summaries, and evidence, not data in your schema's shape.

```json theme={null}
{
  "name": "Competitor product lineup",
  "target": {
    "type": "extract",
    "url": "https://example.com",
    "instructions": "Track product names, plan tiers, and feature limits. Ignore blog posts and job listings.",
    "max_pages": 20,
    "max_depth": 2
  }
}
```

## Update a target

`PATCH /v1/monitors/{monitor_id}` replaces `target` as a whole, so send every field you want to keep, including `instructions` and selectors. Send `change_detection` together with `target`. If you leave it out, the monitor keeps its current detection type even when the new target implies another one. For example, adding `instructions` to an exact monitor without `"change_detection": {"type": "semantic"}` leaves it exact.

```json theme={null}
{
  "target": {
    "type": "page",
    "url": "https://example.com/legal/terms",
    "instructions": "Report changes to data retention, liability, or termination terms.",
    "include_selectors": ["article"]
  },
  "change_detection": { "type": "semantic" }
}
```

Any change to `target` or `change_detection`, including `confidence_threshold`, discards the baseline and queues a new baseline run, which reports no change. Updating `name`, `tags`, `schedule`, `status`, or `webhook` keeps the baseline.

## Limits and errors

An unsupported target and detection pair, or a target URL that isn't public, returns `400 INPUT_VALIDATION_ERROR` with a `details` list of each problem. See [Limits and errors](/monitors/limits-and-errors) for every limit and error code.

## Related

* [Monitors overview](/monitors/overview): create a monitor with a webhook.
* [Runs and changes](/monitors/runs-and-changes): what each target's changes contain.
* [Update a monitor reference](/api-reference/monitors/update)
