# USPTO Trademark Watch: New Filings Leads & Brand Alerts (`kingly_contest_7cb/uspto-trademark-watch`) Actor

Daily USPTO trademark filings as scored DTC brand leads, plus brand-watch alerts for look-alike marks, oppositions and status changes.

- **URL**: https://apify.com/kingly_contest_7cb/uspto-trademark-watch.md
- **Developed by:** [Carina Hatton](https://apify.com/kingly_contest_7cb) (community)
- **Categories:** Lead generation, E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 new filings

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## USPTO Trademark Watch: New Filings Leads & Brand Alerts

Watches the public USPTO trademark search index and gives you two things:

1. **New-filings lead feed.** Fresh US trademark applications, filtered by keyword, class, owner, state, entity type, filing basis and date. Each filing gets a transparent **DTC lead score** that marks likely early-stage consumer brands.
2. **Brand-watch alerts.** New filings and newly published marks that look or sound like the marks you watch, each with a **similarity score and plain-English reason**. You also get **status-change alerts** (published for opposition, registered, abandoned, other status changes) for serials you track. Alerts include the 30-day **opposition deadline**.

Results are de-duplicated across runs, so a scheduled daily run only returns what is new. There is an optional Slack or webhook digest.

> Source: USPTO. Data comes from the USPTO's public Trademark Search system (tmsearch.uspto.gov) and TSDR status pages. This Actor is not affiliated with or endorsed by the USPTO. Similarity scores are a screening aid, **not legal advice** and not a likelihood-of-confusion opinion. Ask a trademark attorney before acting on an alert.

### What it does

| | New-filings feed (`mode: newFilings`) | Brand watch (`mode: brandWatch`) |
|---|---|---|
| Scans | Applications filed in your date window | Same window, plus marks published for opposition in the last N days, plus your watched serials |
| Output | One `filing` row per new application | One `watchAlert` row per new match or status event |
| Ranking | DTC lead score (0–100) with reasons | Similarity score (0–100) with reasons |
| Charged event | `new-filing` | `watch-alert` |

`mode: both` runs both in one pass and shares the same scan.

### Quick start

**Lead feed: yesterday's apparel and cosmetics filings from small US brands**

```json
{
  "mode": "newFilings",
  "lookbackDays": 2,
  "classes": ["3", "25"],
  "countries": ["UNITED STATES"],
  "minLeadScore": 70,
  "maxResults": 200,
  "profileId": "daily-dtc-feed"
}
```

To add public contact details, set `"enrichContacts": true` and `"maxEnrichments": 25`. Enrichment is off by default and is charged per successful lookup.

**Brand watch: alert me about look-alike or sound-alike marks**

```json
{
  "mode": "brandWatch",
  "lookbackDays": 7,
  "watchMarks": [
    {"mark": "YOURBRAND", "classes": ["3", "25"], "label": "Client A", "ignoreOwners": ["Your Company LLC"]}
  ],
  "similarityThreshold": 80,
  "oppositionWatch": true,
  "oppositionLookbackDays": 7,
  "watchSerials": ["50001046"],
  "slackWebhookUrl": "https://hooks.slack.com/services/..."
}
```

Schedule either input daily, keeping the same `profileId`. Each run then returns only filings and alerts it has not delivered before. Use `"mode": "both"` to run the feed and the watch in one pass.

### Input highlights

- **Date window.** Set `lookbackDays` (default 3), or set `dateFrom`/`dateTo` (YYYY-MM-DD, US Eastern). The maximum range is 92 days.
- **Feed filters**:
  - `keywords` (whole word, in the mark or in the mark plus goods/services) and `excludeKeywords`;
  - `classes` (e.g. `"25"`), `owners`, `states` (codes or names), `countries`;
  - `entityTypes`, `filingBases` (`1a`, `1b`, `44d`, `44e`, `66a`), `proSeOnly`, `minLeadScore`, `maxResults`.
- **Brand watch**:
  - `watchMarks`: `[{"mark": "GLOWLAB", "classes": ["3"], "label": "Client A", "ignoreOwners": ["Glowlab"]}]`;
  - `similarityThreshold` (default 80);
  - `oppositionWatch` with `oppositionLookbackDays`;
  - `watchSerials`;
  - `trackStatusOfMatches`, which adds matched serials to status tracking automatically.
- **De-dupe.** `onlyNew` (default on) uses a named key-value store (`stateStoreName`) with one state record per `profileId`. Use a different `profileId` for each client or watch list.
- **Contact enrichment** (`enrichContacts`) is **off by default**. When on, it fetches the public TSDR status page for up to `maxEnrichments` filings (optionally only those with `enrichMinLeadScore` or higher). It adds the attorney name and email, the correspondent name and email(s), the phone, and the TM5 status. **Mailing and domicile addresses are never output.**
- **USPTO API key** (`usptoApiKey`, optional and secret). If you provide one, enrichment uses the TSDR API (`tsdrapi.uspto.gov`) instead of public status pages.
- **Digest.** `slackWebhookUrl` (Slack incoming webhook) and/or `webhookUrl` (receives JSON). The digest contains no emails or phone numbers.
- **Politeness.** `requestIntervalSecs` (default 1.0, minimum 0.5) sets the minimum gap between USPTO search requests. Enrichment waits at least 1.5 s between status pages. Requests back off on 429/5xx errors and honour `Retry-After`.

### Output

Everything goes to the default dataset, with `recordType` set to `filing` or `watchAlert`. The dataset has three views: **Leads**, **Watch alerts** and **Overview**. A `RUN_SUMMARY` record in the default key-value store holds counts for the run.

Example `filing` row (real record from a local test run; identifying fields shortened):

```json
{
  "recordType": "filing",
  "serialNumber": "50185733",
  "mark": "SEDUCED",
  "filedDate": "2026-10-28",
  "classes": ["025"],
  "ownerName": "SEDUCED LLC",
  "ownerEntityType": "LIMITED LIABILITY COMPANY",
  "ownerCity": "Albuquerque",
  "ownerState": "NM",
  "ownerCountry": "UNITED STATES",
  "isProSe": true,
  "filingBasis": ["1b"],
  "leadScore": 85,
  "leadScoreReasons": ["consumer-product class 025 (apparel/footwear)", "US-based owner",
    "LLC or individual owner (small-brand profile)", "filed without an attorney of record",
    "intent-to-use basis (likely pre-launch)", "word mark present"],
  "tsdrUrl": "https://tsdr.uspto.gov/#caseNumber=50185733&caseType=SERIAL_NO&searchType=statusSearch",
  "source": "Source: USPTO"
}
```

Example `watchAlert` row:

```json
{
  "recordType": "watchAlert",
  "alertType": "STATUS_CHANGE",
  "serialNumber": "50001046",
  "mark": "HMU",
  "previousStatus": "APPROVED FOR PUBLICATION",
  "status": "PUBLISHED FOR OPPOSITION",
  "publishedForOppositionDate": "2026-10-20",
  "oppositionDeadline": "2026-11-19",
  "matchReason": "status: APPROVED FOR PUBLICATION -> PUBLISHED FOR OPPOSITION",
  "source": "Source: USPTO"
}
```

Alert types:

- `SIMILAR_FILING`
- `SIMILAR_PUBLISHED_FOR_OPPOSITION`
- `PUBLISHED_FOR_OPPOSITION`
- `REGISTERED`
- `ABANDONED`
- `STATUS_CHANGE`

### How the scores work

**DTC lead score** (capped at 100). Each reason is listed on the row:

- +30 for a consumer-product class: 003, 005, 009, 014, 018, 020, 021, 024, 025, 028, or 029–034;
- +10 for class 035 when the goods text mentions online retail or e-commerce;
- +15 for a US-based owner;
- +10 for an LLC or individual owner;
- +15 for no attorney of record;
- +10 for an intent-to-use (1b) basis;
- +5 for more than one class;
- +5 when a word mark is present.

"No attorney of record" is a proxy for pro se filings, based on the attorney field in the search index.

**Similarity score.** The Actor takes the highest of these signals:

- an exact match after normalisation (punctuation, stopwords like THE/CO/LLC, and digit-to-letter swaps are stripped);
- the watched mark appearing inside the candidate, or the reverse;
- Jaro-Winkler and Levenshtein spelling similarity;
- a simplified phonetic key (very short keys only count when the spelling is also close);
- token-level near matches.

If you set classes on a watch mark and the candidate shares none of them, the score is multiplied by 0.7.

### Pricing (pay per event)

You pay only for the events below. All prices are in USD.

| Event | When it is charged | Price |
|---|---|---|
| `run-start` | Once per run | $0.005 |
| `new-filing` | Per new filing row delivered in the lead feed | $0.003 |
| `contact-enrichment` | Per filing where a public TSDR contact lookup succeeded (opt-in) | $0.004 |
| `watch-alert` | Per brand-watch alert row (similar filing, similar mark published for opposition, or a status change on a watched serial) | $0.05 |

- Filings already delivered to the same `profileId` are not delivered or charged again. Failed contact lookups are not charged.
- Set a **maximum cost per run** in the run options. The Actor stops cleanly when it reaches that cap, and only filings it actually delivered are marked as seen, so the next run picks up the rest.
- Example arithmetic from the prices above: one run that delivers 100 new filings with 10 contact lookups costs $0.005 + 100 × $0.003 + 10 × $0.004 = $0.345.
- On the first run of a new watch profile, marks you track that are already published or have recently changed status can produce several alerts at once.

### Measured data (test runs, 2026-10-30)

These are counts from the USPTO search index, as observed on 2026-10-30:

- Applications by filing date: Oct 23: 2,135; Oct 26: 2,274; Oct 27: 2,453; Oct 28: 2,402; Oct 29: 2,408. Weekend days are lower (Oct 25: 626).

- Filings appear about one day after the filing date.

- Marks published for opposition from Oct 23 onward: 10,787 (the Oct 27 Official Gazette).

- A one-day feed limited to classes 3 and 25 returned 357 filings from 1 search request in about 5 s.

- A 7-day brand watch (5 marks, 12,813 filings scanned, plus the 10,787-mark opposition scan) took 52 search requests and about 98 s.

- On the Apify platform at 512 MB, a one-day class-25 feed capped at 50 rows took 6.0 s. A 3-day brand watch with 3 marks (7,263 filings scanned plus the 10,787-mark opposition scan) took 62.9 s.

Your timings will vary with USPTO load and the request interval.

### Disclaimer

- **Source: USPTO.** The data comes from public USPTO records.
- This Actor is **not affiliated with, endorsed by, or sponsored by the United States Patent and Trademark Office**.
- Similarity scores and alerts are an automated screening aid and **not legal advice**. Talk to a trademark attorney about oppositions, clearance or enforcement.

### Privacy and responsible use

- Domicile, home and mailing addresses are never output. Only the city, state and country come from the search index.
- Free-text owner fields are scrubbed of email addresses. Contact details appear only when you turn enrichment on, and they come from the USPTO's public record.
- If you contact applicants, follow anti-spam law (for example CAN-SPAM) and USPTO guidance on solicitations. Do not imply any USPTO affiliation.

### Local development

```bash
python -m venv .venv && . .venv/bin/activate && pip install -r requirements.txt pytest
python -m pytest -q            # unit tests (offline)
apify validate-schema
ACTOR_TEST_PAY_PER_EVENT=true apify run   # uses storage/key_value_stores/default/INPUT.json
python tests/mock/mock_webhook.py 18766 /tmp/received.jsonl   # local digest receiver
```

# Actor input Schema

## `mode` (type: `string`):

newFilings = lead feed of new applications. brandWatch = alerts for marks similar to your watch list, opposition publications and status changes. both = run both in one pass.

## `lookbackDays` (type: `integer`):

Filing dates to cover, counted back from today (US Eastern), used when 'Filed from' is empty. USPTO search data typically shows a filing about one day after it is filed, so keep at least 2-3 days and rely on dedupe.

## `dateFrom` (type: `string`):

Optional fixed start filing date. Overrides 'Look back'.

## `dateTo` (type: `string`):

Optional end filing date (inclusive). Defaults to today (US Eastern). Max range 92 days.

## `keywords` (type: `array`):

Keep filings whose mark (or goods/services text, see scope) contains any of these words. Empty = no keyword filter.

## `keywordScope` (type: `string`):

Where keywords are matched.

## `excludeKeywords` (type: `array`):

Drop filings containing any of these words.

## `classes` (type: `array`):

Keep filings in any of these classes, e.g. 3, 25, 35. Empty = all classes.

## `owners` (type: `array`):

Keep filings whose owner/applicant name contains any of these strings (case-insensitive).

## `states` (type: `array`):

Two-letter codes or full names, e.g. FL, Texas. Based on the owner's state of address as listed by USPTO.

## `countries` (type: `array`):

Country names as USPTO lists them, e.g. UNITED STATES, CANADA, CHINA. 'US'/'USA' also work.

## `entityTypes` (type: `array`):

E.g. LIMITED LIABILITY COMPANY, INDIVIDUAL, CORPORATION, PARTNERSHIP, OTHER. Substring match.

## `filingBases` (type: `array`):

1a = use in commerce, 1b = intent to use, 44d, 44e, 66a.

## `proSeOnly` (type: `boolean`):

Approximation: no attorney of record is listed in USPTO search data.

## `minLeadScore` (type: `integer`):

0-100 heuristic. See README for the scoring rules.

## `maxResults` (type: `integer`):

Stop the lead feed after this many new filings are output.

## `watchMarks` (type: `array`):

Brand-watch list. Each item: {"mark": "GLOSSIER", "classes": \["3"], "label": "client A"}. Classes are optional; when set, matches outside them are down-weighted. Optional per mark: "ignoreOwners": \["Your Company"] to skip filings by those owners (e.g. your own).

## `similarityThreshold` (type: `integer`):

Alert when the similarity score (0-100) is at or above this value.

## `oppositionWatch` (type: `boolean`):

Also compare watch marks against marks published in the Official Gazette during the last N days (opposition deadline = publication date + 30 days).

## `oppositionLookbackDays` (type: `integer`):

Publication dates to scan for opposition alerts.

## `watchSerials` (type: `array`):

Application serial numbers to monitor for status changes (published, registered, abandoned, other status changes).

## `trackStatusOfMatches` (type: `boolean`):

Automatically add filings that triggered a similarity alert to the status-tracking list.

## `onlyNew` (type: `boolean`):

Dedupe across runs using a named key-value store.

## `profileId` (type: `string`):

Separate dedupe/alert state per profile (e.g. one per client). Use letters, digits, - and \_.

## `stateStoreName` (type: `string`):

Named key-value store used for cross-run state.

## `enrichContacts` (type: `boolean`):

Off by default. When on, fetches each output filing's public TSDR status page and adds attorney/correspondent name, e-mail and phone. Street addresses are never collected. You are responsible for lawful use (e.g. CAN-SPAM, GDPR) of any contact data.

## `maxEnrichments` (type: `integer`):

Upper bound on TSDR lookups per run.

## `enrichMinLeadScore` (type: `integer`):

Saves cost by enriching only higher-scoring leads.

## `usptoApiKey` (type: `string`):

Optional. If set, enrichment uses the official TSDR API (tsdrapi.uspto.gov) with your key instead of the public status page.

## `slackWebhookUrl` (type: `string`):

Optional. Posts a short digest (no e-mails or phone numbers) after each run.

## `webhookUrl` (type: `string`):

Optional. Receives a JSON POST with summary counts and the top items.

## `digestMaxItems` (type: `integer`):

Max filings and max alerts listed in the digest.

## `requestIntervalSecs` (type: `number`):

Polite throttle for USPTO requests. Enrichment never goes faster than 1.5 s (public pages) or 1.0 s (API key).

## Actor input object example

```json
{
  "mode": "newFilings",
  "lookbackDays": 3,
  "keywords": [
    "skincare",
    "apparel"
  ],
  "keywordScope": "markAndGoods",
  "excludeKeywords": [],
  "classes": [
    "3",
    "25"
  ],
  "owners": [],
  "states": [],
  "countries": [],
  "entityTypes": [],
  "filingBases": [],
  "proSeOnly": false,
  "minLeadScore": 0,
  "maxResults": 1000,
  "watchMarks": [
    {
      "mark": "GLOWLAB",
      "classes": [
        "3"
      ],
      "label": "Client A",
      "ignoreOwners": [
        "Glowlab"
      ]
    }
  ],
  "similarityThreshold": 80,
  "oppositionWatch": true,
  "oppositionLookbackDays": 7,
  "watchSerials": [],
  "trackStatusOfMatches": true,
  "onlyNew": true,
  "profileId": "default",
  "stateStoreName": "uspto-trademark-watch-state",
  "enrichContacts": false,
  "maxEnrichments": 50,
  "enrichMinLeadScore": 0,
  "digestMaxItems": 20,
  "requestIntervalSecs": 1
}
```

# Actor output Schema

## `leads` (type: `string`):

No description

## `alerts` (type: `string`):

No description

## `summary` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "keywords": [
        "skincare",
        "apparel"
    ],
    "classes": [
        "3",
        "25"
    ],
    "watchMarks": [
        {
            "mark": "GLOWLAB",
            "classes": [
                "3"
            ],
            "label": "Client A",
            "ignoreOwners": [
                "Glowlab"
            ]
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("kingly_contest_7cb/uspto-trademark-watch").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "keywords": [
        "skincare",
        "apparel",
    ],
    "classes": [
        "3",
        "25",
    ],
    "watchMarks": [{
            "mark": "GLOWLAB",
            "classes": ["3"],
            "label": "Client A",
            "ignoreOwners": ["Glowlab"],
        }],
}

# Run the Actor and wait for it to finish
run = client.actor("kingly_contest_7cb/uspto-trademark-watch").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "keywords": [
    "skincare",
    "apparel"
  ],
  "classes": [
    "3",
    "25"
  ],
  "watchMarks": [
    {
      "mark": "GLOWLAB",
      "classes": [
        "3"
      ],
      "label": "Client A",
      "ignoreOwners": [
        "Glowlab"
      ]
    }
  ]
}' |
apify call kingly_contest_7cb/uspto-trademark-watch --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,kingly_contest_7cb/uspto-trademark-watch"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/eygsnaq4X6d4auQwX/builds/GdliftGmNRnYBz9zh/openapi.json
