# Trademark Search: USPTO, TMview, Madrid, IP Australia (`s-r/trademark-search`) Actor

One row per trademark mark from USPTO, TMview, Madrid and IP Australia: mark text, owner, live/dead/pending status with the office's own wording kept, serial and registration numbers, Nice classes, goods and services text, dates, and per-source status flags.

- **URL**: https://apify.com/s-r/trademark-search.md
- **Developed by:** [SR](https://apify.com/s-r) (community)
- **Categories:** Business, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$6.00 / 1,000 trademark mark rows

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

## Trademark Search Actor: one row per mark across five public sources

This trademark search actor returns one row per mark from the public
registers: mark text, owner, a live / dead / pending verdict with the office's
own wording kept alongside it, serial and registration numbers, Nice classes,
goods and services text, filing and expiry dates, territories of protection,
opposition windows and, on US files, document, assignment and TTAB proceeding
counts. Search by mark name across USPTO, TMview, WIPO Madrid, IP Australia and
the WIPO Global Brand DB, or look up a serial, registration number or Madrid
international registration number.

### What you get

- One dataset row per mark, so a brand sweep is billable only for what you
  actually received.
- A normalised `status_norm` of `live`, `dead` or `pending`, plus `status_raw`
  carrying the office's own wording. USPTO rows keep the TM5 Common Status
  Descriptor (`LIVE/REGISTRATION/Issued and Active`,
  `DEAD/APPLICATION/Refused/Dismissed or Invalidated`), TMview and Brand DB
  rows keep `Registered`, `Filed`, `Ended` or `Expired`, IP Australia rows keep
  `Registered: Registered/protected`.
- Serial number and registration number as separate fields, so a filed mark is
  still identifiable.
- Nice class numbers as a clean integer list, plus the goods and services
  wording from the full record where the register will hand it over.
- Application date, registration date and expiry date in ISO form, and the
  status date where the office prints one.
- `t_protection`, the territories a mark is protected in.
- `mark_type`, the office's own wording for word, figurative or combined marks.
- `opposition_period_start` and `opposition_period_end`, where the register
  publishes the opposition window.
- `documents_count`, `assignments_count` and `proceedings_count` for US files,
  with the TTAB proceeding descriptions in `proceedings`. The fastest way to
  tell a long prosecution from a short one, and an opposition or cancellation
  from a clean file.
- `seniority`, the earlier-mark claims an EU record carries.
- Per-source status flags (`ok`, `not_found`, `rate_limited`, `blocked`) so you
  can tell a mark that does not exist from a register that did not answer.
- A deep link to the record in `source_url`.

### Why scrape trademark registers

Marketplace integrity teams and e-commerce legal counsel run trademark search
by name across their own catalogue, and the question they need answered is
binary. A listing sells brand B, the US mark is dead and only a foreign
registration in class 25 remains, so the listing goes down. A comparative
metric, such as "92% of listings carry a registered mark", never forces a
takedown.

Doing that check by hand means opening five registers, each with its own search
form, its own status vocabulary and its own numbering scheme. USPTO speaks in
TM5 descriptors, TMview and the Brand DB in `Registered` and `Ended`, Madrid
Monitor in official actions, IP Australia in `Registered/protected`. A human
can reconcile five vocabularies for one mark. Doing it for a thousand listings
every week is not a job for a person with a browser.

Grey-import detection, class-conflict checks before a launch and watch sweeps
over a competitor's portfolio all reduce to the same shape: a mark name in, and
out comes the owner, the classes, the status and the dates.

### Input

| Field | Required | What it does |
|---|---|---|
| `mark` | One of `mark` or `number` | Word mark to search for, e.g. `NIKE` or `CREATE-A-CUP`. |
| `number` | One of `mark` or `number` | Exact identifier: a US application serial (`78888801`), a US registration number (`3000000`), or a Madrid international registration number (`1000000`). |
| `number_kind` | No | `auto` works the type out from the shape of the number. The explicit options force it. |
| `mode` | No | `lookup` (default) resolves one mark by identifier. `search` runs a word-mark query. `classes` returns the Nice class headings instead of marks. |
| `offices` | No | Comma separated office codes: `US`, `EM`, `WO`, `AU`, `GB`, `FR`, `DE`, `JP`, `CA`, `IN`, `BX`, or any two-letter office code the indexes carry. Empty means every office. |
| `nice_classes` | No | Comma separated Nice classification numbers, 1 to 45. Empty means all classes. With `mode: "classes"` it selects which headings come back. |
| `max_rows` | No | Upper bound per source, 1 to 500. Default 50. |
| `include_goods_services` | No | Fetch the goods and services wording and the US file's document lists. On by default. |

Give `mark` and `number` together and both a word search and an exact lookup
run in the same call.

### Output

```json
{
  "mark": "CREATE-A-CUP",
  "owner": "Playtex Products, Inc.",
  "status_norm": "dead",
  "status_raw": "DEAD/APPLICATION/Refused/Dismissed or Invalidated",
  "serial": "78888801",
  "registration_number": null,
  "office": "US",
  "office_name": "United States (USPTO)",
  "nice_classes": [21],
  "goods_services": "Children's spill-proof drinking cup with removable insert for drawing/coloring by child",
  "application_date": "2006-05-22",
  "registration_date": null,
  "expiry_date": null,
  "t_protection": [],
  "st13": null,
  "source": "tsdr",
  "source_url": "https://tsdr.uspto.gov/statusview/sn78888801",
  "documents_count": 17,
  "assignments_count": 1,
  "proceedings_count": 0,
  "proceedings": [],
  "mark_type": "Trademark",
  "status_date": "2007-10-23",
  "publication_date": "2007-10-23",
  "opposition_period_start": null,
  "opposition_period_end": null,
  "seniority": [],
  "input": "78888801",
  "source_status": {"tsdr": "ok", "tmview": "ok", "madrid": "skipped", "ipaustralia": "skipped", "branddb": "skipped"}
}
```

Absent facts stay absent. A `null` means the register did not say, and an empty
list means nothing came back. Nothing is filled in from guesswork.

### Use cases

**Marketplace integrity and grey-import checks.** Run a seller catalogue's
brand names through one call and read `status_norm` per row. Rows that come
back `dead` in the home market while another office still shows a live
registration are the listings that need a second look. The `source_status` map
keeps a blocked register from looking like a clean bill of health.

**Class-conflict screening before a launch.** Filter on `nice_classes` and read
the classes and goods and services wording of what is already registered there.
A mark live in class 25 and filed in class 35 is a different risk from one
covering only class 35, and the wording tells you how wide the claim is. With
`mode: "classes"` you can pull the official class headings first and decide
which classes the screen should cover.

**Portfolio watch sweeps.** Look up a registration number and read
`documents_count`, `assignments_count`, `proceedings_count`, `expiry_date` and
`status_raw` together. A US file with a long document list and an expiry date
inside the renewal window is being maintained. The same mark with
`DEAD/APPLICATION`, two documents and an open TTAB proceeding is not.

**Catalogue-wide brand protection reporting.** Because each row is one mark
with its own status, the aggregate is a count rather than an opinion. How many
of the brands we sell hold a live US registration in the relevant class is a
number, and a number is what an enforcement meeting works from.

**Opposition and cancellation checks.** `proceedings_count` and `proceedings`
carry the TTAB docket entries of a US file, and `opposition_period_end` shows
how long an EU mark stays open to opposition. A mark in its opposition window
is a different conversation with a seller than one already registered.

### How it compares

| | This actor | jeeves/uspto-trademark-database | dltik/euipo-trademarks | parseforge/ip-australia-trademarks |
|---|---|---|---|---|
| Sources | USPTO, TMview, Madrid, IP Australia, WIPO Global Brand DB | USPTO only | TMview (70+ offices) | IP Australia only |
| Per 1k rows | $6.00 | $2.00 | $10.00, $20.00 with detail | $5.00 |
| Goods and services text | In the row | No | Separate paid detail call | No |
| TM5 status descriptor kept | Yes | No | No | No |
| Madrid IRN lookup | Yes | No | Via the office filter | No |
| US documents, assignments, TTAB counts | Yes | No | No | No |
| Per-source status flags | Yes | No | No | No |

The closest competitor by scope is `dltik/euipo-trademarks-scraper`, at $10.00
per 1k result rows and a further $10.00 per 1k for the detail call carrying
goods and services. This actor folds that detail into the row, so it sits 40%
below their result-only rate and 70% below their full row. The cheapest
per-item competitor is `jeeves_is_my_copilot` at $2.00 per 1k, USPTO alone.
`parseforge/ip-australia-trademarks-scraper` covers Australia alone at $5.00
per 1k. What they have that we do not: the single-office actors are simpler if
you only ever care about one register, and `dltik` offers tiered pricing that
gets cheaper as you scale.

### Pricing

All pricing is pay-per-event at $0.006 per `trademark`, one event per returned
mark row. $6.00 per 1,000 rows. Rows that carry no mark facts, such as the
placeholder returned when nothing matched, the Nice class headings and the rows
naming a register that could not be read, are delivered but never charged. All
pricing is pay-per-event, you only pay for results you receive. No actor-start
fee, no per-compute-unit charges.

### Limits and gotchas

- USPTO publishes a ceiling near one request per two seconds on its status,
  document and list pages. The actor holds to it and backs off when the office
  asks for more space, so a US lookup takes a few seconds longer than one that
  never reaches the US file. `retries` is not the lever here; the pacing is.
- The TMview search index caps a single query at 10,000 rows. A broad word
  search is partitioned by filing-date window rather than cut off at that
  ceiling, which is why a very common word costs more requests than a rare one.
- The WIPO Global Brand DB is a word-mark door. It carries over 76 million
  marks across far more offices than any other source here, but it takes mark
  text only, so a serial or registration number lookup never reaches it.
- `max_rows` bounds what comes back, not what is searched. Set it low for a
  quick check and high for a sweep.
- `include_goods_services` adds one request per row for the goods wording,
  capped at 40 per run, and fetches the US document, assignment and proceeding
  lists. Turn it off when you only need status and classes.
- Five national registers are closed to unattended callers: UK IPO (GB), INPI
  (FR), DPMA (DE), J-PlatPat (JP) and CIPO (CA). Their marks still arrive
  through the shared multi-office indexes, with the mark's own office on the
  row. Ask one of those offices explicitly when the indexes have nothing for
  it, and you get a row naming the office and the reason, with `blocked` in
  `source_status`, rather than silence.
- Madrid Monitor is only reached when the input looks like an international
  registration number. Madrid has no unattended word-mark search, and the mark
  wording of an international registration is filled in from the shared index
  record of the same number.
- A Madrid file lists one entry per official action, so `status_raw` is the
  action that determines the state of the registration rather than simply the
  newest row.
- Run time follows how many sources fire and how many rows you ask for. A
  single-number lookup returns in seconds; a 50-row word search with goods and
  services takes around half a minute.

### FAQ

**Can I run a trademark search by name rather than by number?**
Yes. Put the name in `mark` and leave `number` empty. The word search runs
against TMview, the WIPO Global Brand DB and IP Australia, up to `max_rows`
rows per source.

**Does this give me a trademark search for the European Union?**
Yes, through the `EM` office. TMview carries the EU register plus more than
seventy others, and the Brand DB reaches further still. Narrow it with
`offices=EM` for EU records only.

**Can I do a trademark lookup Australia only?**
Yes. Set `offices=AU` and the run consults IP Australia plus the shared
indexes filtered to `AU`, returning `Registered: Registered/protected` as
`status_raw` and the registration number.

**What happens when I search a brand that has no registration at all?**
One row with `source: "none"` and `not_found` per register consulted. Nothing
is invented, and the row is not charged.

**How do I check a Madrid international registration?**
Put the number in `number` with `number_kind` set to `irn`, or enter it and let
`auto` classify it. You get the holder, the mark wording, the classes, the
registration date and the official actions, and the status is derived from
those actions.

**Is merkenregister data included for the Dutch market?**
Yes. The Benelux register sits inside the `EM` search and is also reachable on
its own as `offices=BX`, so a mark registered in the Netherlands comes back
with its territories of protection listed.

**Can I see whether a US mark is in an opposition or cancellation?**
Yes. `proceedings_count` and `proceedings` carry the TTAB docket entries, and
`assignments_count` shows the assignment chain length. Both are on by default
and cost nothing extra in row count.

**Can I look up what Nice class 25 covers?**
Yes. Set `mode` to `classes`, optionally with `nice_classes`, and the run
returns the official class headings. Those rows are reference data and are not
charged.

### Related Actors

- [Google Patents Scraper](https://apify.com/s-r/google-patents) for prior art
  and invention records alongside your mark research.
- [SEC EDGAR Scraper](https://apify.com/s-r/sec-edgar-scraper) for filings and
  full-text search over US public companies.
- [FDA Recalls Scraper](https://apify.com/s-r/fda-recalls-scraper) for
  regulatory recall records across food, drug and device.

# Actor input Schema

## `mark` (type: `string`):

Word mark to search for across the registers, e.g. NIKE or CREATE-A-CUP. Leave empty to look up a number instead.

## `number` (type: `string`):

Exact identifier. A US application serial (78888801), a US registration number (3000000), or a Madrid international registration number (1000000). Optional when mark text is given.

## `number_kind` (type: `string`):

What the number field holds. Auto works it out from the shape; the explicit options force it.

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

Lookup resolves one mark by identifier. Search runs a word-mark query and returns up to max\_rows rows. Classes returns the Nice classification headings instead of marks.

## `offices` (type: `string`):

Comma separated office codes to consult. US (USPTO), EM (EU), WO (Madrid), AU (IP Australia), GB, FR, DE, JP, CA, IN, BX, or any two-letter office code the shared indexes carry. Empty means every office.

## `nice_classes` (type: `string`):

Comma separated Nice classification numbers, 1 to 45, to filter on. Empty means all classes.

## `max_rows` (type: `number`):

Upper bound on rows returned per source, 1 to 500. Broad mark searches are partitioned by filing-date window rather than cut off at the index ceiling.

## `include_goods_services` (type: `boolean`):

Fetch the goods and services wording from the full record where it is reachable. Adds one request per row, capped at 40 per run.

## Actor input object example

```json
{
  "mark": "NIKE",
  "number": "78888801",
  "number_kind": "auto",
  "mode": "lookup",
  "offices": "US,EM,AU",
  "nice_classes": "25,35",
  "max_rows": 50,
  "include_goods_services": true
}
```

# Actor output Schema

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

One row per mark, with status, owner, classes, goods and services, dates and per-source flags.

## `output` (type: `string`):

OUTPUT record with the run's counts, status flags and the marks found.

## `errors` (type: `string`):

Failures with a code and a redacted message. Absent when the run had none.

# 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 = {
    "mark": "NIKE",
    "number": "",
    "offices": "",
    "nice_classes": ""
};

// Run the Actor and wait for it to finish
const run = await client.actor("s-r/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 = {
    "mark": "NIKE",
    "number": "",
    "offices": "",
    "nice_classes": "",
}

# Run the Actor and wait for it to finish
run = client.actor("s-r/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 '{
  "mark": "NIKE",
  "number": "",
  "offices": "",
  "nice_classes": ""
}' |
apify call s-r/trademark-search --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,s-r/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/HU89CHdCe0fmTas81/builds/CZFUeFtdTsLW8P8iZ/openapi.json
