---
title: "Vehicle watches"
description: "Saved vehicle searches matched against every auction source your key can use and, if it has classifieds, the European classifieds — scanned every 10 minutes, with new matches delivered by polling or a signed webhook. Scope: watches. Granted per contract — not on a new key by default. Up to 10 criteria per watch and 25 active watches per key."
canonical: "https://thecarapi.com/docs/watches"
contract_version: "2026-10-05"
api_base: "https://api.thecarapi.com"
source: "https://thecarapi.com/docs/watches.md"
---

# Vehicle watches

Saved vehicle searches matched against every auction source your key can use and, if it has classifieds, the European classifieds — scanned every 10 minutes, with new matches delivered by polling or a signed webhook. Scope: watches. Granted per contract — not on a new key by default. Up to 10 criteria per watch and 25 active watches per key.

## From a watch to a match

1. Create the watch with POST /api/watches and keep the webhook_secret from the response — it is shown only here and when rotated.
2. The first scan records what already matches, flagged initial: true. Later scans add only cars listed since.
3. Either poll GET /api/watches/{watch_id}/matches with after_id set to the last next_after_id you saw, or receive each batch of new matches on your webhook_url.
4. Verify every webhook delivery: X-Watch-Signature is sha256=<hex HMAC-SHA256 of "<X-Watch-Timestamp>.<raw body>"> under your webhook_secret.

## GET /api/watches

The calling key’s watches, with their match counts.

**Scope:** `watches`

### Example requests

```http
GET https://api.thecarapi.com/api/watches
```

### Example response

```json
{
  "success": true,
  "watches": [
    {
      "id": 7,
      "name": "Diesel 3 Series under 20k",
      "criteria": [
        {
          "make": "BMW",
          "model": "3 Series",
          "fuel": "Diesel",
          "year_from": 2018,
          "max_price": 20000
        }
      ],
      "include_classifieds": true,
      "webhook_url": "https://example.com/hooks/watch",
      "webhook_enabled": true,
      "active": true,
      "match_count": 132
    }
  ],
  "contract_version": "2026-10-05"
}
```

## POST /api/watches

Save a vehicle configuration to keep searching for.

**Scope:** `watches`

### Parameters

| Parameter | Send in | Type | Required | Example | Where the value comes from | Description |
| --- | --- | --- | --- | --- | --- | --- |
| `criteria` | JSON body | object \| object[] | yes | `{"make": "BMW", "model": "3 Series", "fuel": "Diesel", "year_from": 2018, "max_price": 20000}` | /api/brands, /api/models, /api/fuels, /api/gearboxes | One criterion or a list of up to 10; a car matching any of them matches the watch. Each needs make, and may give model, fuel, gearbox, country (ISO or europe), year_from / year_to, kilometers_from / kilometers_to, horsepower_from / horsepower_to (auctions only) and price_from / max_price. |
| `name` | JSON body | string | no | `Diesel 3 Series under 20k` | Your label. | Free text. |
| `include_classifieds` | JSON body | boolean | no | `true` | Your choice. | Default true. Also scan the European classifieds — only when the key has the classifieds scope. |
| `webhook_url` | JSON body | string | no | `https://example.com/hooks/watch` | Your receiver. | https on a public address. Each batch of new matches is POSTed here, signed. |

### Example requests

```http
POST https://api.thecarapi.com/api/watches
{"name": "Diesel 3 Series under 20k", "criteria": [{"make": "BMW", "model": "3 Series", "fuel": "Diesel", "year_from": 2018, "max_price": 20000}], "webhook_url": "https://example.com/hooks/watch"}
```

### Example response

```json
{
  "success": true,
  "watch": {
    "id": 7,
    "name": "Diesel 3 Series under 20k",
    "criteria": [
      {
        "make": "BMW",
        "model": "3 Series",
        "fuel": "Diesel",
        "year_from": 2018,
        "max_price": 20000
      }
    ],
    "include_classifieds": true,
    "webhook_url": "https://example.com/hooks/watch",
    "webhook_enabled": true,
    "webhook_secret": "3f9c…",
    "active": true
  },
  "contract_version": "2026-10-05"
}
```

**Notes.** Answers 201. price_from and max_price compare against the landed-in-Bulgaria price, which includes duty and import VAT for cars outside the EU; every match also carries the source price. A webhook that fails 20 times in a row is switched off; matches keep accumulating for polling. A 26th active watch is a 409.

## GET /api/watches/{watch_id}

One watch.

**Scope:** `watches`

### Parameters

| Parameter | Send in | Type | Required | Example | Where the value comes from | Description |
| --- | --- | --- | --- | --- | --- | --- |
| `watch_id` | URL path | integer | yes | `7` | /api/watches -> watches[].id | The watch id. |

### Example requests

```http
GET https://api.thecarapi.com/api/watches/7
```

### Example response

```json
{
  "success": true,
  "watch": {
    "id": 7,
    "name": "Diesel 3 Series under 20k",
    "active": true,
    "match_count": 132
  },
  "contract_version": "2026-10-05"
}
```

## PATCH /api/watches/{watch_id}

Change a watch.

**Scope:** `watches`

### Parameters

| Parameter | Send in | Type | Required | Example | Where the value comes from | Description |
| --- | --- | --- | --- | --- | --- | --- |
| `watch_id` | URL path | integer | yes | `7` | /api/watches -> watches[].id | The watch id. |
| `name / criteria / include_classifieds` | JSON body | string \| object[] \| boolean | no | `criteria` | As on create. | New criteria restart the baseline: the next scan records what matches then as initial. |
| `webhook_url / webhook_enabled` | JSON body | string \| boolean | no | `""` | Your receiver. | An empty webhook_url removes it. |
| `active` | JSON body | boolean | no | `false` | Your choice. | Pause or resume scanning. |
| `rotate_secret` | JSON body | boolean | no | `true` | Your choice. | true issues a new webhook_secret, returned in this response only. |

### Example requests

```http
PATCH https://api.thecarapi.com/api/watches/7
{"active": false}
```

```http
PATCH https://api.thecarapi.com/api/watches/7
{"rotate_secret": true}
```

### Example response

```json
{
  "success": true,
  "watch": {
    "id": 7,
    "active": false
  },
  "contract_version": "2026-10-05"
}
```

## DELETE /api/watches/{watch_id}

Delete a watch and its matches.

**Scope:** `watches`

### Parameters

| Parameter | Send in | Type | Required | Example | Where the value comes from | Description |
| --- | --- | --- | --- | --- | --- | --- |
| `watch_id` | URL path | integer | yes | `7` | /api/watches -> watches[].id | The watch id. |

### Example requests

```http
DELETE https://api.thecarapi.com/api/watches/7
```

### Example response

```json
{
  "success": true,
  "contract_version": "2026-10-05"
}
```

## GET /api/watches/{watch_id}/matches

Matches found for a watch, in the order they were found.

**Scope:** `watches`

### Parameters

| Parameter | Send in | Type | Required | Example | Where the value comes from | Description |
| --- | --- | --- | --- | --- | --- | --- |
| `watch_id` | URL path | integer | yes | `7` | /api/watches -> watches[].id | The watch id. |
| `after_id` | Query string | integer | no | `0` | The last next_after_id you received. | Stable cursor, so nothing is missed or repeated. Default 0. |
| `include_initial` | Query string | boolean | no | `false` | Your choice. | Default true. false skips what already matched when the watch was created. |
| `source` | Query string | enum | no | `auction` | auction or classified. | Only matches from one side. |
| `limit` | Query string | integer | no | `100` | Page size. | Default 100, maximum 500. |

### Example requests

```http
GET https://api.thecarapi.com/api/watches/7/matches?after_id=0
```

```http
GET https://api.thecarapi.com/api/watches/7/matches?after_id=1880&include_initial=false
```

### Example response

```json
{
  "success": true,
  "matches": [
    {
      "id": 1881,
      "source": "auction",
      "initial": false,
      "matched_at": "2026-10-18T09:20:00Z",
      "vehicle": {
        "site": "openlane",
        "auction_id": 11409652,
        "title": "BMW 320d Touring",
        "price_eur": 16900,
        "landed_price_eur": 18450,
        "api_url": "/api/auction/openlane/11409652"
      }
    }
  ],
  "next_after_id": 1881,
  "has_more": false,
  "contract_version": "2026-10-05"
}
```

### Response fields

| Field | Type | Meaning |
| --- | --- | --- |
| `matches[].vehicle` | object | The car as it looked when found, with api_url (auctions) or url (classifieds, the original ad) to read it now. |
| `next_after_id / has_more` | integer / boolean | Send next_after_id as after_id next time; has_more says whether to ask again straight away. |
