# USPTO Trademark Batch Search API (`khadinakbar/uspto-trademark-batch-search`) Actor

Batch-search the USPTO trademark register by keyword, serial number, registration number, or owner. Returns live/pending/dead status, Nice classes, owners, dates, goods/services, and TSDR links. HTTP-only, no API key. Built for POD clearance, brand monitoring, and AI agents.

- **URL**: https://apify.com/khadinakbar/uspto-trademark-batch-search.md
- **Developed by:** [Khadin Akbar](https://apify.com/khadinakbar) (community)
- **Categories:** Developer tools, Automation, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 uspto trademark results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## USPTO Trademark Batch Search API

Batch-search the United States Patent and Trademark Office register by keyword, serial number, registration number, or owner name. Each query returns matching federal marks with live/pending/dead status, Nice classes, owner, filing and registration dates, goods and services, and a TSDR link — or one CLEAR row when nothing matches. Designed for print-on-demand sellers, brand-clearance workflows, trademark monitoring, and AI agents that need a structured USPTO answer without a USPTO.gov login or API key.

This Actor reads the public Trademark Search index behind [tmsearch.uspto.gov](https://tmsearch.uspto.gov). It is a search and status lookup, not a legal opinion and not a substitute for a comprehensive attorney-led clearance search.

### Best fit for this Actor

- You have a list of brand phrases, merch slogans, or domain names and need to know whether live or pending US federal marks already use them.
- You already have serial or registration numbers and want structured status, owner, class, and TSDR links in one dataset.
- You want an owner-portfolio snapshot (for example every live mark owned by `Nike, Inc.`) capped to a predictable number of rows.
- For patents, citations, and family graphs, use [Google Patents Scraper](https://apify.com/khadinakbar/google-patents-scraper) instead of this Actor.

### POD clothing clearance for a merch phrase

A merch seller is about to print `STAR WARS` on class 25 apparel. They pass that phrase with `niceClasses` set to `25` and `statusFilter` set to `live`. The dataset comes back with `matchFound: true`, `recordStatus: LIVE`, Lucasfilm as `ownerName`, serial `75306994`, and a `tsdrUrl` they can open. They drop the phrase from the catalog and keep the TSDR link in the audit trail. A nonsense phrase in the same batch returns `recordStatus: CLEAR`, so the rest of the list can ship.

### Quick start input

```json
{
    "searchTerms": ["star wars"],
    "niceClasses": "25",
    "statusFilter": "live",
    "maxResultsPerTerm": 3,
    "includeClearRows": true
}
```

Each value in `searchTerms` is one USPTO query. `maxResultsPerTerm` keeps a famous mark from dumping hundreds of rows into the dataset; `maxResults` is the run-wide cost ceiling.

### Input reference

| Field | Type | What it controls |
|---|---|---|
| `searchTerms` | array | Wordmark/keyword phrases, up to 100. Example: `star wars`. |
| `serialNumbers` | array | Exact 7-8 digit serials, `SN:` prefixes, or TSDR URLs. |
| `registrationNumbers` | array | Exact registration numbers such as `2239077`. |
| `ownerNames` | array | Owner-portfolio search, up to 50 names. |
| `statusFilter` | enum | `live` (default), `pending`, `registered`, `dead`, or `all`. |
| `niceClasses` | string | Comma-separated Nice classes 1-45. Empty = every class. `25` is clothing. |
| `matchMode` | enum | `contains` (default) or `exact` wordmark. Ignored for serial/registration lookup. |
| `maxResultsPerTerm` | integer | 1-50 rows saved per query. Default 5. Prefill 3. |
| `maxResults` | integer | Run-wide row cap, 1-1000. Default 250. |
| `includeClearRows` | boolean | Write a CLEAR row when a query has zero matches. Default true. |
| `maxConcurrency` | integer | Parallel USPTO queries, 1-8. Default 4. |

At least one of `searchTerms`, `serialNumbers`, `registrationNumbers`, or `ownerNames` is required. Empty input returns `INVALID_INPUT` with zero rows and zero `trademark-result` charges.

### What data you receive

One dataset item is either a matching USPTO mark or a CLEAR no-match row for a query.

```json
{
    "searchTerm": "star wars",
    "queryType": "wordmark",
    "matchFound": true,
    "recordStatus": "LIVE",
    "wordmark": "STAR WARS",
    "serialNumber": "75306994",
    "registrationNumber": "2534385",
    "statusDescription": "REGISTERED AND RENEWED",
    "niceClasses": [25],
    "ownerName": "Lucasfilm Ltd. LLC (LIMITED LIABILITY COMPANY; CALIFORNIA, USA)",
    "filingDate": "1997-06-11",
    "tsdrUrl": "https://tsdr.uspto.gov/#caseNumber=75306994&caseSearchType=US_APPLICATION&caseType=SERIAL_NO&searchType=statusSearch",
    "source": "uspto-tmsearch",
    "scrapedAt": "2026-08-17T10:00:00.000Z"
}
```

`recordStatus` values:

| Value | Meaning |
|---|---|
| `LIVE` | Alive and registered |
| `PENDING` | Alive but not yet registered |
| `DEAD` | Abandoned, cancelled, or expired |
| `CLEAR` | This query produced no matches under the chosen filters |

`matchCountForQuery` is the USPTO hit total before the per-query cap, so you can see that `STAR WARS` in class 25 had thousands of hits even when only three rows were saved.

### Real-world use cases

#### 1. POD clothing batch

Check a merch phrase list against live class 25 marks before upload.

```json
{
    "searchTerms": ["star wars", "just do it", "zzzxqwertyplugh999"],
    "niceClasses": "25",
    "statusFilter": "live",
    "maxResultsPerTerm": 3,
    "includeClearRows": true
}
```

Use `matchFound` and `recordStatus` to drop conflicts. Keep CLEAR phrases.

#### 2. Exact wordmark, all classes

Confirm whether the literal mark `NIKE` is live anywhere, not just as a substring of longer marks.

```json
{
    "searchTerms": ["NIKE"],
    "matchMode": "exact",
    "statusFilter": "live",
    "maxResultsPerTerm": 5
}
```

#### 3. Serial number status

Look up a known filing from a TSDR URL or raw serial.

```json
{
    "serialNumbers": ["74612654"]
}
```

Returns owner, classes, registration number, and the canonical TSDR link.

#### 4. Registration number lookup

Resolve a certificate number to the current index record.

```json
{
    "registrationNumbers": ["2239077"],
    "statusFilter": "all"
}
```

#### 5. Owner portfolio

List live marks for one registrant, capped so the run stays cheap.

```json
{
    "ownerNames": ["Nike, Inc."],
    "statusFilter": "live",
    "maxResultsPerTerm": 10,
    "maxResults": 10
}
```

#### 6. Pending-only watch

See only applications that are alive but not yet registered.

```json
{
    "searchTerms": ["just do it"],
    "statusFilter": "pending",
    "maxResultsPerTerm": 5
}
```

#### 7. Dead-mark history

Check whether a name was used before and later abandoned or cancelled.

```json
{
    "searchTerms": ["twin spray"],
    "statusFilter": "dead",
    "maxResultsPerTerm": 5,
    "includeClearRows": false
}
```

#### 8. Mixed batch for an agent

Combine a phrase, a serial, and an owner in one run.

```json
{
    "searchTerms": ["star wars"],
    "serialNumbers": ["74612654"],
    "ownerNames": ["Lucasfilm Ltd."],
    "niceClasses": "25",
    "statusFilter": "live",
    "maxResultsPerTerm": 3,
    "maxResults": 20,
    "maxConcurrency": 2
}
```

### Why agents choose this Actor

- Bounded cost: `$0.005` per saved row plus a `$0.00005` start event, with `maxResults` as the hard ceiling.
- Flat JSON keys (`wordmark`, `serialNumber`, `recordStatus`, `tsdrUrl`) instead of nested USPTO XML.
- CLEAR rows make a 100-term batch complete even when most phrases are free of live hits.
- Provenance is explicit: `source` is always `uspto-tmsearch`, `scrapedAt` is ISO, `tsdrUrl` points at the official case.
- Terminal `OUTPUT.outcome` values (`COMPLETE`, `PARTIAL`, `VALID_EMPTY`, `INVALID_INPUT`, `UPSTREAM_FAILED`) are stable enough to branch on.
- Serial, registration, owner, and keyword search share one schema, so a tool call does not need a mode enum.
- Patent work is out of scope on purpose, which keeps this tool from colliding with [Google Patents Scraper](https://apify.com/khadinakbar/google-patents-scraper).

### Agent checklist

1. Pass trademark phrases, serials, registration numbers, or owner names. Patent numbers belong on Google Patents Scraper.
2. Set `maxResults` (and `maxResultsPerTerm`) to the budget you can spend at `$0.005` per row.
3. Prefer `statusFilter: "live"` and a Nice class such as `"25"` for merch clearance.
4. After the run, read `OUTPUT.outcome`, then the dataset. An empty dataset is a valid CLEAR/no-match result when the outcome is `VALID_EMPTY`.
5. Open `tsdrUrl` when you need the official status page or documents; this Actor does not download TSDR PDFs.
6. If `matchCountForQuery` is larger than the rows you received, raise the caps or narrow `niceClasses` / `matchMode`.
7. Schedule repeats against the same `searchTerms` when you are monitoring new filings; USPTO search is not a live opposition-calendar feed.

### Use through the API

```bash
curl "https://api.apify.com/v2/acts/khadinakbar~uspto-trademark-batch-search/runs?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "searchTerms": ["star wars"],
    "niceClasses": "25",
    "statusFilter": "live",
    "maxResultsPerTerm": 3
  }'
```

When the Actor completes, read dataset items from the default dataset and the `OUTPUT` record from the default key-value store.

### Use with AI agents through Apify MCP

> Check these merch phrases against live USPTO class 25 trademarks: "star wars", "just do it". Return wordmark, serialNumber, recordStatus, ownerName, niceClasses, and tsdrUrl. Cap at 3 matches per phrase and keep CLEAR rows. Then inspect OUTPUT.outcome before summarizing.

Connect through <https://mcp.apify.com>. After the tool call, read the dataset and the `OUTPUT` / `RUN_SUMMARY` records. Preserve `tsdrUrl` as the official source link.

### Pricing

This Actor uses Pay per event plus Apify platform usage. Open the live Pricing tab for current event details, and use Apify's run cost controls to keep the workflow aligned with your budget.

Worked examples at the launch prices (`apify-actor-start` $0.00005, `trademark-result` $0.005):

| Input | Rows billed | Event cost |
|---|---|---|
| One phrase, 3 matches | 3 | ~$0.015 |
| 10 phrases, all CLEAR | 10 | ~$0.050 |
| 100 phrases, 1 row each | 100 | ~$0.500 |
| Serial lookup, 1 hit | 1 | ~$0.005 |
| Empty / invalid input | 0 | start event only |

Formula: `trademark-result count × $0.005 + start event + platform usage`. Platform usage is billed to the user on top of events.

### Outcome vocabulary

These values live in the key-value store records `OUTPUT` and `RUN_SUMMARY`, not on each dataset row.

| Outcome | Meaning |
|---|---|
| `COMPLETE` | Every query was processed and useful rows were saved. |
| `PARTIAL` | Some queries were incomplete, a cap stopped the run, or the event limit was hit; saved rows are still in the dataset. |
| `VALID_EMPTY` | Queries succeeded and produced zero rows (usually `includeClearRows: false` with no matches). |
| `INVALID_INPUT` | Nothing to search. Fix the input; no trademark events are charged. |
| `UPSTREAM_FAILED` | USPTO Trademark Search did not return usable data for the requested work. |

### Connect the workflow

- After you confirm a brand name is clear at USPTO, enrich the seller or importer with [ImportYeti Scraper](https://apify.com/khadinakbar/importyeti-scraper) when you also need US Customs shipment history and listed trademarks.
- For patent — not trademark — search, citations, and family graphs, use [Google Patents Scraper](https://apify.com/khadinakbar/google-patents-scraper).
- To see whether a brand name is being used in live Google Search ads, continue with [Google Ads Scraper](https://apify.com/khadinakbar/google-ads-scraper).

### Best results

- Filter by Nice class for POD and product clearance. Class 25 is clothing; class 9 is electronics; class 18 is bags.
- Use `contains` for risk spotting and `exact` when you care about the literal wordmark.
- Keep `maxResultsPerTerm` small (3-5) on famous marks. `STAR WARS` in class 25 has thousands of live hits.
- Leave `includeClearRows` on for batch jobs so every input phrase appears in the export.
- USPTO search can lag new filings by a short index delay. Re-run if you are checking a mark filed in the last few days.

### Builder's note

I built this after posting live queries to the public Trademark Search index. I found the UI is a thin client over an Elasticsearch-style POST whose envelope uses `hits.totalValue` and `source` (not `_source`), and that `_source: true` can come back empty. In my testing, mapping `alive` plus `registered` into `LIVE` / `PENDING` / `DEAD` was more useful for agents than raw USPTO status codes. My goal was a complete 100-phrase batch, which is why a query with zero hits still writes a CLEAR row instead of going missing from the export. When AWS WAF challenges a datacenter IP, the run retries once through Apify Residential US and keeps only real USPTO rows.

### FAQ

**How many trademarks do I get per phrase?**
Up to `maxResultsPerTerm` matching rows, plus one CLEAR row when `includeClearRows` is on and nothing matches. Famous marks such as STAR WARS can have thousands of hits; the cap keeps the export and the bill predictable.

**Do I need a USPTO API key or login?**
No. The Actor queries the public Trademark Search index over HTTP. You never supply a USPTO account, cookie, or API key.

**What if USPTO is temporarily unavailable?**
The run retries, then tries Apify Residential US. When no advertised query returns usable data and no rows were saved, the run ends `UPSTREAM_FAILED`. Useful partial results still succeed as `PARTIAL`.

**Can an AI agent use this?**
Yes. It is MCP-ready: pass phrases, serials, registration numbers, or owner names and read one row per match (or CLEAR). Inspect `OUTPUT.outcome` before summarizing.

**Is this legal advice or a full clearance search?**
No. Results are public-register data for research and monitoring. Pair them with a trademark attorney before filing or launching a brand.

### Responsible use

This Actor returns public USPTO trademark records for clearance research, monitoring, and enrichment. Treat the output as public-register data: pair it with a trademark attorney before filing or launching a brand, keep request volume courteous to the public search service, and use results in line with USPTO terms and applicable laws.

# Actor input Schema

## `searchTerms` (type: `array`):

Words or phrases to check against USPTO wordmarks, for example 'star wars' or 'just do it'. Each term is searched separately, up to 100 per run. Use this for POD/brand clearance, not for patent search and not for downloading TSDR prosecution PDFs.

## `serialNumbers` (type: `array`):

USPTO serial numbers to look up exactly, such as 74612654. Accepts 7-8 digits, an SN prefix, or a TSDR URL with caseNumber. Maximum 100 per run. Use this when you already know the filing number; do not put brand phrases here.

## `registrationNumbers` (type: `array`):

USPTO registration numbers to look up exactly, such as 2239077 or RN:2239077. Maximum 100 per run. This is the registration identifier on a live or dead certificate, not the 8-digit serial number.

## `ownerNames` (type: `array`):

Owner or applicant names to search, for example 'Nike, Inc.'. Returns that owner's marks subject to status and class filters. Maximum 50 per run. This is an owner-portfolio search, not a goods/services keyword search.

## `statusFilter` (type: `string`):

Which USPTO live/dead states to return. Default live includes registered and pending filings and is the usual POD clearance filter. pending is live-but-unregistered; dead is abandoned/cancelled/expired; all includes history. This is not a legal-risk score.

## `niceClasses` (type: `string`):

Optional comma-separated Nice Classification numbers from 1 to 45, for example '25' for clothing or '25,9,18'. Leave empty to search every class. Class 25 is the common POD clothing filter; this is not a US design-code or coordinated-class filter.

## `matchMode` (type: `string`):

How searchTerms are matched against USPTO wordmarks. contains (default) finds the phrase inside combined mark text, so 'star wars' also returns STAR WARS GALACTIC STARCRUISER. exact prefers the literal wordmark. Serial and registration lookups ignore this field.

## `maxResultsPerTerm` (type: `integer`):

Maximum matching trademark rows saved for each search term, serial, registration, or owner query. Default 5 keeps POD batches cheap; raise toward 50 for owner portfolios. This caps output per query, not the entire USPTO hit count.

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

Hard cap on dataset rows for the whole run, including CLEAR rows. Default 250. Use this as the cost ceiling: each saved row is one $0.005 trademark-result event. This is not pagination into a previous run.

## `includeClearRows` (type: `boolean`):

When enabled (default), a query with zero matches still writes one row with matchFound=false and recordStatus=CLEAR so a 100-term batch stays complete. Turn this off when you only want conflicting marks. CLEAR rows are billed the same as matches.

## `maxConcurrency` (type: `integer`):

How many USPTO queries to run in parallel. Default 4 balances speed against USPTO courtesy limits. Lower to 1 if you see HTTP 429 in the log; raise to 8 only for large owner or serial batches.

## Actor input object example

```json
{
  "searchTerms": [
    "star wars"
  ],
  "statusFilter": "live",
  "niceClasses": "25",
  "matchMode": "contains",
  "maxResultsPerTerm": 3,
  "maxResults": 20,
  "includeClearRows": true,
  "maxConcurrency": 2
}
```

# Actor output Schema

## `results` (type: `string`):

Dataset items containing search term, match/CLEAR status, wordmark, serial, owner, Nice classes, dates, and TSDR URL.

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

Run summary including outcome, itemsPushed, per-query results, USPTO status codes, warnings, and billing counters.

## `runSummary` (type: `string`):

Machine-readable RUN\_SUMMARY record mirroring OUTPUT for integrations that read RUN\_SUMMARY directly.

# 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 = {
    "searchTerms": [
        "star wars"
    ],
    "statusFilter": "live",
    "niceClasses": "25",
    "matchMode": "contains",
    "maxResultsPerTerm": 3,
    "maxResults": 20,
    "includeClearRows": true,
    "maxConcurrency": 2
};

// Run the Actor and wait for it to finish
const run = await client.actor("khadinakbar/uspto-trademark-batch-search").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 = {
    "searchTerms": ["star wars"],
    "statusFilter": "live",
    "niceClasses": "25",
    "matchMode": "contains",
    "maxResultsPerTerm": 3,
    "maxResults": 20,
    "includeClearRows": True,
    "maxConcurrency": 2,
}

# Run the Actor and wait for it to finish
run = client.actor("khadinakbar/uspto-trademark-batch-search").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 '{
  "searchTerms": [
    "star wars"
  ],
  "statusFilter": "live",
  "niceClasses": "25",
  "matchMode": "contains",
  "maxResultsPerTerm": 3,
  "maxResults": 20,
  "includeClearRows": true,
  "maxConcurrency": 2
}' |
apify call khadinakbar/uspto-trademark-batch-search --silent --output-dataset

```

## MCP server setup

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

```

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/gtlMIaxo9Jgp92hXY/builds/oheHCBPrYhex03LOl/openapi.json
