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

# Migrating to the new search parameters

> Move from strategies, match and q lists to similarity, text filters and exclusions

On September 29, 2026, trademark search moved to one request model. `q` always ranks, `similarity` picks how, text filters decide which marks qualify, and exclusions leave marks out. This guide maps every old parameter to its replacement.

The old parameters keep working until **December 28, 2026**. Until then, each response that used one lists it in `search_meta.deprecations`, with its replacement and the sunset date. From December 28 they return `400` with `unknown_parameter`, naming the replacement.

This applies to `GET` and `POST /v1/trademarks`, the owner, attorney, firm and entity `/trademarks` lists, similarity watches, the MCP `search_trademarks` tool and TypeScript SDK 0.17.0.

## What changes without a code change

Some changes reach every integration on the release date:

* **Default recall is wider.** `q` without `similarity` runs `identical`, `fuzzy`, `embedded` and `lookalike`. The old default was `exact,fuzzy`. You now also get marks with a different first letter (LIMBER for `timber`), look-alike characters (TIMB3R) and prefix phrases. Abbreviation matching and owner-name matching are gone; search owners with [Search Owners](/api-reference/parties/search-owners). `search_meta.ranking_version` reads `v12`.
* **Cursors restart.** Cursors issued before the release return `400 cursor_invalid`. Start again from the first page.
* **Scores survive `sort`.** With `q`, `relevance_score` and `match` are on every hit under any `sort`. They used to be `null`.
* **Timeouts fail instead of returning a partial page.** A search that hits its time limit or loses a shard returns a retryable `503`. Set `allow_partial=true` to get the rows found so far with `search_meta.complete: false`. The `partial_results` warning is gone.
* **No quiet downgrades.** A query too expensive to run returns `400` with `code: too_complex`. It used to be retried with fewer strategies and a `strategies_reduced` warning.
* **`mark_text_not_contains` ignores punctuation and spacing**, like the other text filters, and takes repeated keys for several values.
* **Skip warnings name a channel.** A channel that can't run on a `q` adds `<channel>_skipped`, such as `phonetic_skipped`, with the channel in `channel`.

## Parameter map

| Old | New | Notes |
| - | - | - |
| `strategies=exact` | `similarity=identical,lookalike` | The old `exact` also folded look-alike letters |
| `strategies=fuzzy` | `similarity=fuzzy,embedded,lookalike` | The old `fuzzy` also matched phrases, containment and look-alikes |
| `strategies=phonetic` | `similarity=phonetic` | `identical` is always added |
| `strategies=prefix` | `similarity=embedded` | |
| `strategies=exact,fuzzy` (old default) | Omit `similarity` | The new default is wider |
| `match=similar` | Remove it | `q` always ranks |
| `match=exact&q=nike` | `mark_text_is=nike` | |
| `match=starts_with&q=nik` | `mark_text_starts_with=nik` | |
| `match=ends_with&q=ike` | `mark_text_ends_with=ike` | Now needs 3 characters |
| `match=contains&q=nik` | `mark_text_contains=nik` | Counts characters after punctuation is removed |
| `q=["nike","mike"]` | `mark_text_is=nike&mark_text_is=mike` | Repeated keys, never comma-split |
| `mark_text_not_contains=sunset` | `mark_text_not_contains=sunset` | Same name. Now a list, folded like the other filters |
| POST `query` | POST `q` | |
| POST `mark_text_not_contains` | POST `exclude.mark_text.contains` | |
| `ranking_profile` | Remove it | Choose channels with `similarity` |
| Watch `strategies` | Watch `similarity` | Not translated: the watch stays legacy until re-saved with `similarity` |
| Watch `min_match_tier` | Watch `min_tier` | `identical`, `near_identical`, `similar` or `related`. Not translated: the watch stays legacy until re-saved |

Translated requests never return fewer marks than before. That is why a translated `strategies` set can turn on more channels than its name suggests: the old strategies ran more matching than their names said. To get exactly the channels you want, send `similarity` yourself.

## Response fields

| Old | New |
| - | - |
| `match_explanation.strategies_matched` | `match.via` (channels) and `match.tier` |
| `matched_terms` | `match.terms` |
| `search_meta.strategies_used` | `search_meta.similarity_applied` |
| `search_meta.match` | The text filters you sent, and `search_meta.order` |
| `search_meta.warnings[].code: partial_results` | `search_meta.complete` and `search_meta.incomplete_reason` |
| `search_meta.warnings[].code: strategies_reduced` | Removed. The request fails with `too_complex` instead |

`match_explanation`, `matched_terms`, `search_meta.strategies_used` and `search_meta.match` keep being filled from the new data until December 28, 2026, so existing parsers keep working. Switch to `match` before then. The fine-grained labels that `strategies_matched` used to show are in `match.details.lanes` with `include=match_details`.

## Before and after

### Choose the matching

```bash theme={null}
# Before
curl -G "https://api.signa.so/v1/trademarks" \
  -H "Authorization: Bearer $SIGNA_API_KEY" \
  --data-urlencode "q=nova" \
  --data-urlencode "strategies=exact,fuzzy,phonetic,prefix"

# After
curl -G "https://api.signa.so/v1/trademarks" \
  -H "Authorization: Bearer $SIGNA_API_KEY" \
  --data-urlencode "q=nova" \
  --data-urlencode "similarity=identical,fuzzy,embedded,phonetic,lookalike"
```

### Starts with, ends with, contains

`match` replaced `q` with a text test. The text filters are separate parameters, so they combine with a ranked `q` of a different value.

```bash theme={null}
# Before: marks starting with NOV, by date, no score
curl -G "https://api.signa.so/v1/trademarks" \
  -H "Authorization: Bearer $SIGNA_API_KEY" \
  --data-urlencode "q=nov" \
  --data-urlencode "match=starts_with"

# After: the same marks, in text order (NOV first, then shortest)
curl -G "https://api.signa.so/v1/trademarks" \
  -H "Authorization: Bearer $SIGNA_API_KEY" \
  --data-urlencode "mark_text_starts_with=nov"

# New: marks resembling NOVA that also start with NOV, ranked
curl -G "https://api.signa.so/v1/trademarks" \
  -H "Authorization: Bearer $SIGNA_API_KEY" \
  --data-urlencode "q=nova" \
  --data-urlencode "mark_text_starts_with=nov"
```

A text filter without `q` used to list the newest filings first. It now lists in text order: marks equal to the value, then marks starting with it, then shorter marks, then newer filings. Add `sort=-filing_date` to keep the old order.

### A list of exact spellings

```bash theme={null}
# Before
curl -G "https://api.signa.so/v1/trademarks" \
  -H "Authorization: Bearer $SIGNA_API_KEY" \
  --data-urlencode 'q=["limber","bimber"]'

# After
curl -G "https://api.signa.so/v1/trademarks" \
  -H "Authorization: Bearer $SIGNA_API_KEY" \
  --data-urlencode "mark_text_is=limber" \
  --data-urlencode "mark_text_is=bimber"
```

A cursor from the old form continues under the new form, so you can switch in the middle of a walk.

### POST bodies

<CodeGroup>
  ```json Before theme={null}
  {
    "query": "nova",
    "strategies": ["exact", "phonetic"],
    "mark_text_not_contains": "novartis",
    "filters": { "nice_classes": [9] }
  }
  ```

  ```json After theme={null}
  {
    "q": "nova",
    "similarity": ["identical", "lookalike", "phonetic"],
    "filters": { "nice_classes": [9] },
    "exclude": { "mark_text": { "contains": ["novartis"] } }
  }
  ```
</CodeGroup>

`strategies: ["exact", "phonetic"]` translates to `identical`, `lookalike` and `phonetic`. Drop `lookalike` if you don't want look-alike characters.

### Reading why a mark matched

```typescript theme={null}
// @docs-no-check
// Before
const phonetic = hit.match_explanation?.strategies_matched.includes("phonetic");

// After
const phonetic = hit.match?.via.includes("phonetic");
const strong = hit.match?.tier === "identical" || hit.match?.tier === "near_identical";
```

## TypeScript SDK

SDK 0.17.0 removes the old keys from the types. The API still accepts them from older SDKs until December 28, 2026.

```typescript theme={null}
// @docs-no-check
// Before (0.16 and earlier)
const page = await signa.trademarks.list({
  q: "nova",
  strategies: ["exact", "fuzzy", "phonetic"],
  match: "similar",
});
const variants = await signa.trademarks.list({ q: ["limber", "bimber"] });
const body = await signa.trademarks.search({ query: "nova", strategies: ["exact"] });
```

```typescript theme={null}
// After (0.17.0)
import { Signa, Presets } from "@signa-so/sdk";

const signa = new Signa();

const page = await signa.trademarks.list({ q: "nova", ...Presets.clearance });
const variants = await signa.trademarks.list({ mark_text_is: ["limber", "bimber"] });
const body = await signa.trademarks.search({
  q: "nova",
  similarity: ["identical", "lookalike"],
  filters: { mark_text: { starts_with: ["nov"] } },
  exclude: { entity_id: ["ent_Wp8qLd4Z"] },
});

for (const hit of page.data) {
  console.log(hit.mark_text, hit.relevance_score, hit.match?.tier, hit.match?.via);
}
```

`Presets.knockout` is `identical,fuzzy,embedded`, a fast first screen. `Presets.clearance` adds `phonetic` and `lookalike`. Both spread into `list()` or `search()`. `list()` sends text filters and exclusions as repeated keys and switches to `POST` when the URL would pass 2 KB.

## Watches

Existing watches keep alerting exactly as before. Their matching changes only when you update the query with the new keys. Sending the stored query back unchanged changes nothing.

To use the new matching, send `version: "v3"`, or `similarity` or `min_tier`:

```json theme={null}
{
  "name": "NOVA sound-alikes, US class 9",
  "watch_type": "similarity",
  "query": {
    "version": "v3",
    "q": "nova",
    "similarity": ["identical", "fuzzy", "phonetic"],
    "min_tier": "similar",
    "filters": { "jurisdictions": ["US"], "niceClasses": [9] }
  }
}
```

Watch keys are not translated. A watch written with `strategies` or `min_match_tier` is kept as a legacy watch until you re-save it with the new keys: it is stored as sent and keeps its legacy lane set and tier gate, so it alerts exactly as before. The create, update, bulk and preview responses list each old key in `deprecations`, with its replacement and the 2026-12-28 sunset. Sending an old key with a new one (`similarity`, `min_tier` or `version: "v3"`) returns `400` (`conflicting_params`). Watches don't take text filters or exclusions yet.

Alerts gain `match.tier`, the same label a search hit carries. `match.score` is still the engine score for the watch query, not the 0 to 100 `relevance_score`. See [Watches](/guides/monitoring/watches).

## Checklist

1. Search your code for `strategies`, `match`, `ranking_profile`, `query` in POST bodies, and list values in `q`. Replace each one using the map above.
2. Read `match` instead of `match_explanation` and `matched_terms`.
3. Handle `503` on searches, or send `allow_partial=true` and check `search_meta.complete`.
4. Restart any pagination walk that stored a cursor from before September 29, 2026.
5. Watch your responses for `search_meta.deprecations`. An empty list means the request is fully migrated.
6. Upgrade to SDK 0.17.0.

See [Search Trademarks](/api-reference/trademarks/search-trademarks) for the full reference.
