# US Trademark Search (USPTO) — Marks, Status & Classes (`northbell/us-trademark-search`) Actor

Search the US trademark register (USPTO) by word or by owner company: mark, status, status date, filing and registration dates, Nice classes, goods and services and the company that owns it. Run it again or on a schedule to see new filings and status changes since the last run. No login.

- **URL**: https://apify.com/northbell/us-trademark-search.md
- **Developed by:** [Northbell](https://apify.com/northbell) (community)
- **Categories:** Business, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.80 / 1,000 trademark records

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## US Trademark Search (USPTO) — Marks, Status & Classes

Give it a word or phrase, or an owner company, and get one clean row per USPTO trademark record: **mark, status, filing and registration dates, Nice classes, goods and services and the company that owns it**, with a link to the official TSDR page. **$1.00 per 1,000 records**, no login, no API key.

Run it again — or put it on a daily or weekly schedule — and every row also tells you **what changed since the last run**: `isNew` for a mark that was not in your results before (a new filing), and `statusChanged` with `previousStatus` for a mark whose status moved, for example from `New application - record initialized not assigned to examiner` to `Registered`. The USPTO only shows today's status; this keeps the previous one for you.

### What you get

| Field | What it is |
|---|---|
| `serialNumber`, `registrationNumber`, `url` | the USPTO application (serial) number, the registration number (only once registered) and the link to its TSDR status page |
| `mark`, `markType`, `markFormat` | the wording of the mark (`null` for a design-only mark), its type (`trademark`, `service mark`, `trademark, service mark`, `certification mark`…) and how it was filed (`Standard character mark`, `Typed drawing`, `Design only`…) |
| `status`, `statusCode`, `live`, `stage` | the USPTO's status text, its status code, `true` while the mark is pending or in force, and `stage`: `pending`, `registered` or `dead` (abandoned, cancelled, expired) |
| `statusDate` | the day the current status was set. Only filled when you switch on **Add the status date**. That lookup is slow — about 17 records per minute — and stops after 40 minutes, so on a large pull not every record gets one (see Notes) |
| `filingDate`, `registrationDate`, `publicationDate` | filed, registered and published for opposition. All ISO dates (`YYYY-MM-DD`); `null` when it has not happened |
| `classes`, `goodsServicesSnippet` | the Nice classes as numbers (`[9, 42]`) and the first 300 characters of the goods and services statement |
| `ownerName`, `ownerType`, `ownerEntityType`, `ownerJurisdiction`, `ownerCount` | the **current owner when it is a company**: its name, `company` / `non-profit` / `government`, its legal form (`corporation`, `limited liability company`…), the state or country it is organised in, and how many owners the record lists. People are never named: see Notes |
| `isNew`, `statusChanged`, `previousStatus`, `previousStatusDate`, `previousObservedAt`, `firstSeenAt` | the change since your previous run of the same search. `null` on the first run |
| `query`, `queryType`, `observedAt` | which word (`word`) or owner company (`owner`) found the record, and when it was read |

### Run it again and again

- **On a schedule** — save your input as a task and add a **Schedule**: the same words and owners every week. Send each run to Google Sheets, Slack or email with an integration on the task.
- **Watch for changes** — Keep the same input on the schedule. Each run is its own dataset with the date it ran, so the series builds up run by run and you can compare one day with the last.
- **In bulk** — Put every word in **Words or phrases to search** and every company in **Owner companies** in one run.

### Use it for

- **Name clearance** — before you launch a brand or product name, search it in your Nice class and see which live marks already contain it and who owns them. This is a text search; it is not legal advice or an official clearance search
- **Brand and competitor watch** — schedule it weekly with your own marks and your competitors' names: `isNew` is a new filing, `statusChanged` is a mark that was registered, abandoned or cancelled (a watch sees the records it returns: keep the word specific so that it finds fewer than `maxResults`, see Notes)
- **A company's portfolio** — put the company in **Owner companies** and get the marks it owns today, with classes and status, for due diligence or a competitor review
- **Lead lists** — newly filed marks by companies in a class (filter `isNew` and `stage: pending`) show who is starting a new product line

### Input

```json
{ "queries": ["apify"], "owners": ["Apify Technologies"], "status": "live", "classes": [42], "maxResults": 100 }
```

- **queries** — words or phrases, one per line. **owners** — company names. Fill in either or both (to search owners only, leave **queries** empty). Up to 200 entries per run
- **status** — `any`, `live` or `dead`. **classes** — Nice class numbers, empty for all. A mark filed in several classes is kept when any one of them is in your list, and is returned with all of its classes
- **matchType** — `contains` (default: the mark text contains your words next to each other, in that order), `exact` or `startsWith`. Case is ignored
- **maxResults** — per word and per owner, 1 to 1,000. A record that two entries both find is returned once
- **lookupStatusDate** — adds `statusDate`. Slow: see Notes

What changed is remembered per search (the words and options together) in a named key-value store in your Apify account, so the second run of the same input already shows changes. Schedule it (Apify → Schedules) for a daily or weekly watch.

### Pricing

**$1.00 per 1,000 records**, plus $0.01 per run. Error rows (a search that could not be read) are free. The $0.01 run-start fee is the one charge every run pays, even one that returns nothing.

### Notes

- **The source is the USPTO's own trademark search and case-status service** (the data behind tmsearch.uspto.gov and TSDR), read the way the website reads it. This Actor is not affiliated with the USPTO. The data is what the register shows at `observedAt`; it is not legal advice and not an official certificate.
- **Companies only; people are not named.** `ownerName` is filled only when the current owner is a company or organisation: the applicant's entity label is not a person, and the name carries a company form (Inc., LLC, Ltd, GmbH, S.A. …) or an institution word (university, foundation, state agency …). For a record owned by a person, a sole proprietor or joint owners, `ownerName` is `null` and `ownerType` is `individual`. The entity label is typed in by the applicant and some people are filed as a company, so these are also left out, with `ownerName` `null` and `ownerType` `withheld`: a name with no company form, a partnership of named people, a trust or trustee, a professional practice (`…, P.C.`, `…, D.D.S.`) and a person's "d/b/a" name. A company that carries its founder's name in a legal form (`Firstname Lastname, Inc.`) is still a company and is shown. Addresses, attorneys and correspondents are never put in the output. The current owner is the one the USPTO flags as last listed, so a mark that was sold shows the buyer. A mark whose wording is a person's name is still returned, because that is the mark.
- **Owner search** returns a record only when a company that owns it today carries every word you typed. An owner whose name is withheld (see above) is not found. A person's name finds only companies that have those words in their name, never the person's own marks.
- **It compares text, not sound or look.** Case is ignored, but `BLUEBIRD` and `BLUE BIRD` are different texts, so search both spellings. `contains` needs your words next to each other and in order (`NIKE AIR` is found by `nike air`, not by `air nike`).
- **Status date is slow, so it is off by default.** The USPTO limits how fast one IP address may ask: when I sent 139 requests in 98 seconds on 9 Oct 2026, its firewall answered HTTP 429 and then 403 for about four minutes. So the Actor sends one request every 3.5 seconds. A search returns up to 1,000 records in a single request, but `statusDate` needs one more request per record: about 17 records per minute (20 records plus the search took 72 seconds on 9 Oct 2026). The Actor stops asking for dates after 40 minutes (about 680 records) and after five failed lookups in a row; the records after that come out with `statusDate` `null` and the run log says how many. If the firewall blocks the address anyway, the Actor waits three minutes and tries again (up to four times), then reports the search as an error row.
- **Fields filled only in some cases.** `registrationNumber` and `registrationDate` exist only for registered marks, `publicationDate` only once a mark was published for opposition, and `mark` is `null` for design-only marks. `ownerEntityType` and `ownerJurisdiction` are filled only together with `ownerName` (and `ownerEntityType` is `null` when the applicant typed a whole sentence of more than 100 characters); a few records list no owner at all (`ownerType` is `null`). `classes` is `[]` and `goodsServicesSnippet` `null` when the record lists none. `statusDate` is filled only with **Add the status date**, and a row for which the service had no date keeps `null`. `previousStatus` and `previousObservedAt` are filled only for a record you saw before, `previousStatusDate` only when that earlier run also had **Add the status date** on; `statusChanged` is `null` for a record that is new.
- **What "new" and "changed" mean.** `isNew` is a record that was not among the results of the same search on your previous run. `statusChanged` is a new status code, or, when both runs read the status date, a new status date on the same status. Both only see the records that are returned. When a search finds more records than `maxResults`, the best text matches come first, then the newest filings, so a word that matches thousands of marks is watched through its top `maxResults` only. Use a specific word, or raise `maxResults` above the number of matches (the run log and the `totalMatches` count in the run's OUTPUT say how many there are). Raising `maxResults` later shows the extra records as new once, and at the edge of a big window an older mark can move in for the first time and show as new too: compare `filingDate` to tell a new filing from an old mark. A record that drops out of the window keeps its remembered status (up to 3,000 records are remembered per search), so it is not flagged new when it comes back.
- **Status wording** is the USPTO's text, first letter capital (`Registered and renewed`). `statusCode` is the USPTO's number for it. `goodsServicesSnippet` has email addresses and phone numbers removed.
- **One row per record per run.** If two entries of your input find the same record, it appears once, under the first.

More company data from northbell: [UK Companies House Search](https://apify.com/northbell/companies-house-search), [Company Lookup by Domain](https://apify.com/northbell/company-lookup-by-domain).

### For AI agents

`{"queries": ["apify"], "maxResults": 5}` returns up to 5 rows with mark, status, dates, classes and owner. `{"queries": [], "owners": ["Nike, Inc."], "status": "live"}` returns the live marks that company owns. Rows have `type`: `trademark` or `error`. To monitor, run the same input on a schedule and read `isNew` and `statusChanged`.

# Actor input Schema

## `queries` (type: `array`):

Marks to look for, one per line, for example a brand name. A mark matches when its text contains your words next to each other and in that order (see Match type). Fill in this or Owner companies, or both.

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

Company names, one per line. Returns the marks that company owns today, so a mark that was sold on to someone else is not returned. Only companies and organisations are searched: a record whose current owner is a person is never returned for an owner search.

## `status` (type: `string`):

Keep only live marks (an application in progress, or a registration in force), only dead ones (abandoned, cancelled, expired), or both.

## `matchType` (type: `string`):

How a word or phrase in Words or phrases to search is compared with the mark text. Case is ignored. Does not apply to Owner companies.

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

Keep only marks that cover at least one of these Nice classes (1 to 45), for example 9 for software or 25 for clothing. A mark filed in several classes is returned with all of them. Leave empty for all classes.

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

How many records to return for each word and for each owner company (1 to 1,000). A record that two entries both find is returned once. The best text matches come first, then the newest filings.

## `lookupStatusDate` (type: `boolean`):

Adds statusDate, the day the current status was set, read from the USPTO case-status service. That service gives one record per request, so this is slow: about 17 records per minute, and the Actor stops asking after 40 minutes. Off, statusDate is null and the run takes seconds.

## Actor input object example

```json
{
  "queries": [
    "apify"
  ],
  "status": "any",
  "matchType": "contains",
  "maxResults": 100,
  "lookupStatusDate": false
}
```

# Actor output Schema

## `trademarks` (type: `string`):

Mark, status, filing and registration dates, Nice classes, company owner and the change since the last run.

# 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 = {
    "queries": [
        "apify"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("northbell/us-trademark-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 = { "queries": ["apify"] }

# Run the Actor and wait for it to finish
run = client.actor("northbell/us-trademark-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 '{
  "queries": [
    "apify"
  ]
}' |
apify call northbell/us-trademark-search --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,northbell/us-trademark-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/mWxsote5aud6ynvDD/builds/AKo1t7TH3ONH7Vd0Q/openapi.json
