Skip to main content
POST
/
v1
/
watches
/
preview
curl -X POST "https://api.signa.so/v1/watches/preview" \
  -H "Authorization: Bearer $SIGNA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: preview-owner-watch-2026-06-12" \
  -d '{
    "query": {
      "version": "v1",
      "filters": { "ownerId": "own_018f9b2e-9b6c-7c9c-b4f1-1234567890ab" }
    },
    "trial_window_days": 30
  }'
{
  "object": "watch_preview",
  "estimated_match_count": 17,
  "trial_window_days": 30,
  "request_id": "req_..."
}

Overview

Returns the number of trademarks that would have alerted if this query had been a live watch over the last trial_window_days (default 7). Uses the same evaluation logic as a live watch, so the count is a faithful preview — not a separate query engine. Useful before Create Watch to estimate volume and tune score_threshold for similarity watches.
Preview is a read-shaped operation (it creates nothing), so the Idempotency-Key header is accepted but no longer required. Sending one (as in the example below) is always safe — the header is format-validated and otherwise ignored.

Body Parameters

query
object
required
Same DSL as Create Watch — see the canonical query reference. ID-bearing filters (filters.trademarkIds, filters.ownerId, …) accept the same prefixed (tm_* / own_*) or raw-UUID forms as create.
trial_window_days
integer
Backtest window (1—365). Default 7.

Response

object
string
Always "watch_preview".
estimated_match_count
integer
Trademarks that would have alerted in the trial window.
estimate_basis
string
Present only when the count is an upper-bound estimate rather than an exact count. The single value is "candidacy_upper_bound", set by any of three triggers: the candidacy scan overflowed the server-side cap, the search backend was unreachable (the count falls back to the candidacy scan), or the server-side time budget expired after candidates were found (partial result). When the field is absent, the count is exact.
trial_window_days
integer
Echo of the requested window.
request_id
string
Request identifier.

Latency and the time budget

Preview runs synchronously with a server-side time budget of roughly 20 seconds. Typical latency: class, mark, and owner previews complete in a few seconds; similarity previews over broad scopes and long windows are the heaviest and can approach the budget. When the budget is exceeded there are three cases:
  1. Zero candidates found in the trial window — normal 200 with estimated_match_count: 0 (exact; never a timeout).
  2. Budget expired after candidates were found200 with a partial count: matches counted so far plus an upper bound for the unprocessed remainder, labeled estimate_basis: "candidacy_upper_bound".
  3. Budget (or the database statement timeout) expired before any usable result existed504 with a preview_timeout error envelope and retryable: false. Retrying the same query will hit the same wall — narrow it instead (fewer offices, shorter trial_window_days, tighter filters).

Errors

  • 400 — invalid query (see Create Watch).
  • 413query payload exceeds 256 KB.
  • 429 — another preview is already running for your organization (or the server’s preview capacity is saturated). rate_limited envelope; honor the Retry-After header (roughly the server-side budget, ~20s) before retrying. The official SDK does this automatically. Unlike window 429s (numeric {limit};w={sec} policies), these concurrency 429s replace the policy header with a named policy — RateLimit-Policy: "preview-concurrency";c=1;w=20 when your org’s own preview holds the slot, or "preview-concurrency-global";c=2;w=20 when the instance-wide cap is saturated — where c is the concurrency cap, and the RateLimit header reads remaining=0, reset=20.
  • 504preview_timeout envelope (case 3 above). The body carries retryable: false — do not blind-retry.
504 preview_timeout
{
  "error": {
    "type": "preview_timeout",
    "title": "Preview Timeout",
    "status": 504,
    "detail": "The preview could not produce a result within the server-side time budget.",
    "suggestion": "Narrow the watch query — add office or jurisdiction filters, reduce trial_window_days, or scope filters.trademarkIds — and retry.",
    "retryable": false,
    "retry_after": null
  },
  "request_id": "req_..."
}
curl -X POST "https://api.signa.so/v1/watches/preview" \
  -H "Authorization: Bearer $SIGNA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: preview-owner-watch-2026-06-12" \
  -d '{
    "query": {
      "version": "v1",
      "filters": { "ownerId": "own_018f9b2e-9b6c-7c9c-b4f1-1234567890ab" }
    },
    "trial_window_days": 30
  }'
{
  "object": "watch_preview",
  "estimated_match_count": 17,
  "trial_window_days": 30,
  "request_id": "req_..."
}