---
title: "Get watch matches"
method: POST
path: "/get_watch_matches"
---

# Get watch matches

`POST /get_watch_matches`

## Request

- Base URL: `https://newsmind.app/api/v1`
- URL: `https://newsmind.app/api/v1/get_watch_matches`
- Auth: OAuth 2 (authorization code, authorization URL `https://newsmind.app/oauth/authorize`, token URL `https://newsmind.app/oauth/token`, scopes: `mcp`)

Pull matches newer than each watch's last_seen_at cursor. With watch_id, runs one watch. Without, runs all. Atomically advances cursors when mark_seen=true (default). Subject to tier retention.

## Request body

- object
  - `watch_id` integer: Run a specific watch. Omit to run all of the user's watches.
  - `mark_seen` boolean: If false, peek without marking the returned hits as seen. Default true.
  - `limit` integer: Max articles per watch. Default 50; values outside 1-100 are clamped.

## Response `200`

Success

- object
  - `count` integer: Number of watches run (not articles).
  - `mark_seen` boolean
  - `results` object[]
    - `watch_id` integer
    - `query` string
    - `label` string, nullable
    - `feed_id` integer, nullable
    - `cursor_was` string, date-time
    - `cursor_advanced_to` string, date-time
    - `count` integer
    - `articles` Article[]
      - `id` integer
      - `feed_id` integer: ID of the feed this article belongs to. Useful for chaining into list_feeds or unsubscribe.
      - `feed_title` string, nullable
      - `title` string, nullable
      - `url` string, nullable
      - `image_url` string, uri, nullable: Thumbnail URL for the article, when available. HTTPS-only; tracking pixels filtered out. Sourced from <enclosure>, <media:thumbnail>, <media:content>, or first <img> in content. Forward-only — articles ingested before 0.3.0 have null.
      - `image_width` integer, nullable: Pixel width of image_url, when the source feed exposed it (MRSS attrs or HTML width=). Lets clients lay out before fetching the bytes. Null when unknown.
      - `image_height` integer, nullable: Pixel height of image_url, when known. Null otherwise.
      - `image_mime` string, nullable: MIME type of image_url (image/jpeg, image/png, image/webp, image/gif, image/avif, image/svg+xml). Sourced from <enclosure type=>, <media:content type=>, or inferred from URL extension. Null when neither available.
      - `summary` string, nullable
      - `published_at` string, date-time, nullable: When the source published the article (from the RSS feed).
      - `added_at` string, date-time, nullable: When Newsmind ingested the article into the user's feed. Use this when the user asks about 'when did this show up for me' or 'what's new since I last checked'; use published_at for 'how old is the story'.
      - `matched_watches` object[]: Saved watches this article matched. Present on unread/new-article responses when the user has active watches.
        - `watch_id` integer
        - `label` string, nullable
      - `likely_archival` object: Present when the title states a date a year or more before published_at — an aggregator re-indexed an old page under today's date. Don't present these as new.
        - `title_year` integer
        - `matched_text` string
        - `pattern` 'slash' | 'written' | 'paren'
      - `also_in` object[]: Other feeds that carried this same story. The duplicate was collapsed to one match (issue #59).
        - `feed_id` integer
        - `feed_title` string, nullable
      - `user_excluded` object: This row matched one of the watch's exclude_terms. Demoted, never dropped, and the term is named so an over-broad guess can be corrected (issue #23).
        - `term` string
      - `low_signal` object: Present when a Google News item's source is an encyclopedia, film database, statistics page or obituary listing rather than reporting. `rule: "obituary"` comes from a headline rule and may name a legitimate newsroom — describe the item, not the outlet.
        - `source` string
        - `rule` 'reference' | 'entertainment_db' | 'sports_stats' | 'obituary' | 'archive_page' | 'scraped_video'

## Other responses

- `400`: Bad request
- `401`: Unauthorized
- `402`: Subscription required (trial expired or cancelled)
- `429`: Too many calls. Either the per-account rate limit ({error: "rate_limited", message}) or the account's in-flight work ({error: <sentence>, code: "busy"}). Wait Retry-After seconds.
- `500`: Server error on our side (error: internal_error). The team has been notified.
- `503`: Temporarily unable to answer. error: timeout (the request ran too long; narrow it), server_busy or tier_unavailable (retry after Retry-After seconds when sent).

---

[API](https://skmtc.dev/newsmind/apis/newsmind-api.md) · [All operations](https://skmtc.dev/newsmind/apis/newsmind-api/llms.txt) · [OpenAPI document](https://skmtc.dev/newsmind/apis/newsmind-api/revisions/f8af23fb5a10?raw)
