# POD & KDP Trademark Checker (USPTO) (`vncntlabs/pod-trademark-checker`) Actor

Trademark checker for Etsy, Merch by Amazon, Redbubble, TikTok Shop and KDP sellers. Check listing titles, tags and slogans against 4.4M+ live USPTO trademarks before you upload. Get a high/medium/low risk score per phrase with the mark, owner, classes and TSDR link. Not legal advice.

- **URL**: https://apify.com/vncntlabs/pod-trademark-checker.md
- **Developed by:** [vncntlabs Labs](https://apify.com/vncntlabs) (community)
- **Categories:** E-commerce, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $10.00 / 1,000 listing checkeds

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

## POD & KDP Trademark Checker: Etsy, Merch by Amazon, Redbubble and KDP trademark checker (USPTO)

Check your **print-on-demand (POD), Etsy, Amazon Merch on Demand, Redbubble, Teepublic, Shopify and Amazon KDP**
listing titles and tags against **live U.S. federal trademarks (USPTO)** in the product classes POD sellers
actually sell in. Paste listings or slogans and get a **high / medium / low risk** score for each, plus the exact
trademark behind every flag: mark, serial and registration number, owner, status, classes and a direct USPTO TSDR link.

Find risky phrases **before** you upload. That helps you avoid takedowns, account suspensions and IP complaints.

**Is this phrase trademarked?** Paste it into `phrases` and find out in seconds, for about a third of a cent.

### Ready-made examples by platform

Each link opens a ready-to-run example with sample input and output. Click **Try** to copy it into your account
and replace the sample listings with your own.

| You sell on | What it checks | Example |
|---|---|---|
| **Merch by Amazon** | T-shirt titles and keywords in class 25 (clothing) | [Merch by Amazon trademark checker](https://apify.com/vncntlabs/pod-trademark-checker/examples/merch-by-amazon-trademark-checker) |
| **Etsy** | Titles and all 13 tags, including brand names hidden in tags | [Etsy trademark checker](https://apify.com/vncntlabs/pod-trademark-checker/examples/etsy-trademark-checker) |
| **Redbubble / Teepublic** | Sticker, shirt and phone case designs (classes 16, 25 and 9) | [Redbubble trademark checker](https://apify.com/vncntlabs/pod-trademark-checker/examples/redbubble-sticker-trademark-checker) |
| **Amazon KDP** | Book titles, subtitles and your 7 backend keywords (classes 16 and 9) | [KDP book title trademark check](https://apify.com/vncntlabs/pod-trademark-checker/examples/kdp-book-title-trademark-check) |
| **Any POD shop** | A list of slogans, quotes or design text | [Is this phrase trademarked?](https://apify.com/vncntlabs/pod-trademark-checker/examples/slogan-quote-trademark-checker) |
| **Mugs and tumblers** | Drinkware listings in class 21 | [Mug and tumbler trademark check](https://apify.com/vncntlabs/pod-trademark-checker/examples/mug-tumbler-trademark-check) |
| **Printify / Printful / Shopify** | Store-wide audit, including product descriptions | [POD store trademark audit](https://apify.com/vncntlabs/pod-trademark-checker/examples/printify-printful-store-trademark-audit) |
| **Seasonal (Q4)** | Halloween and Christmas slogans | [Halloween and Christmas shirt trademark check](https://apify.com/vncntlabs/pod-trademark-checker/examples/halloween-christmas-shirt-trademark-check) |
| **New clothing brand** | Brand name ideas in class 25, including spacing variants | [Clothing brand name trademark search](https://apify.com/vncntlabs/pod-trademark-checker/examples/clothing-brand-name-trademark-search) |
| **TikTok Shop** | Product titles, including "dupe" listings that name a brand | [TikTok Shop trademark check](https://apify.com/vncntlabs/pod-trademark-checker/examples/tiktok-shop-trademark-check) |

> ⚠️ **Not legal advice.** This is an automated screening tool. It compares your text with the USPTO's live federal
> trademark records. It does **not** cover state or common-law trademarks, copyrights, design patents, foreign marks,
> trade dress, logos or images, or the right of publicity. It does not judge likelihood of confusion, fair use or
> descriptive use. **No flag does not mean a phrase is safe.** A flag does not mean you are infringing.
> For decisions that matter, talk to a trademark attorney.

### What it does

- **Checks every phrase in a listing.** It extracts every 1–5 word phrase from the title and from each tag, and
  never joins words across a separator such as `|` or `,`, or across two tags.
- **Matches against about 4.4 million live USPTO marks.** That covers registered marks and pending applications,
  in the classes you choose. Dead, cancelled and abandoned marks are excluded.
- **Uses POD classes by default:**
  - 025: clothing (t-shirts, hoodies, hats)
  - 021: mugs and drinkware
  - 016: stickers, paper, posters, books and notebooks
  - 009: phone cases
  - 014: jewelry and keychains
  - 018: bags and totes
  - 020: pillows and decor
  - 024: blankets and textiles
  - 028: toys and games
- **Fuzzy mode** also catches:
  - spacing variants (`justdoit` vs JUST DO IT)
  - singular and plural forms
  - USPTO pseudo-marks (the alternate spellings the USPTO records for a mark)
  - phrases that cover most of a longer mark
  - a leading "the", "a" or "an" written differently ("Elf on the Shelf" vs THE ELF ON THE SHELF)
- **Narrows checks to your product type.** If a listing names its product (shirt, hoodie, mug, sticker,
  phone case, tote, pillow, blanket and so on), only that product's class is checked. That keeps out marks
  registered for unrelated goods. A product word that only describes another one counts as a description, not
  a second product: a "laptop sticker" is a sticker, not a laptop accessory. Turn this off with
  `inferClassesFromListing`.
- **Scores risk so you can act quickly:**

| Risk | Meaning |
|---|---|
| **high** | Your phrase is exactly a live **standard-character (word) mark** for your product's class. Example: "Hakuna Matata" on a t-shirt matches Disney's reg. 2700605 in IC 025. A dictionary word only counts as high when it is a major brand portfolio, such as FORD (Ford Motor Co. holds 135 live FORD marks), or a less common word that one brand dominates, such as YETI (YETI Coolers holds 119 of the 139 live YETI marks). |
| **medium** | It matches the exact wording of a live **stylized or design** mark, a spacing variant ("spiderman" vs SPIDER-MAN), a plural or a USPTO pseudo-mark. It can also be a dictionary word that one owner has registered many times (CAT, owned by Caterpillar Inc.), or an ordinary word registered by several unrelated owners. A dominant-brand word (YETI) is medium when the listing doesn't name its product. Exact word marks are also reported as medium when the phrase is plainly descriptive in your listing: a style or format term ("vintage style", "boho", "dot to dot"), ordinary words plus the product type ("funny mug"), or very common words found only inside a longer phrase ("one more" in "Just One More Chapter"). Large brand owners' marks always stay high. |
| **low** | A common dictionary word, or a variant of ordinary English words, that happens to be registered. Also a phrase that is only the start of a longer mark, or a single word that is already covered by a flagged longer phrase in the same listing ("cat" inside "grumpy cat"). Hidden by default; turn on `includeLowRisk` to see these. |
| **none** | No live mark was found in the selected classes. |

### Input

```json
{
  "listings": [
    {"id": "etsy-1", "title": "Retro Hakuna Matata Shirt, Funny Lion Tee", "tags": ["hakuna matata", "lion shirt"]},
    "Life is Good Camping Hoodie for Outdoor Lovers"
  ],
  "phrases": ["Stay Wild Moon Child"],
  "classes": ["025", "021", "016", "009", "014", "018", "020", "024", "028"],
  "matchMode": "fuzzy",
  "includeLowRisk": false,
  "maxListings": 0
}
```

- **`listings`**: plain title strings, or objects with `title`, `tags`, an optional `id` and an optional
  `description`. Set `checkDescription` to also check the description.
- **`phrases`**: standalone slogans, checked exactly as written as one whole phrase.
- **`classes`**: any Nice classes from 001 to 045.
- **`matchMode`**: `fuzzy` (default) or `exact`.
- **`maxListings`**: a hard cap on the number of items, to limit spend.
- **`confirmWithTsdr`** + **`tsdrApiKey`** (**beta**, optional, off by default): re-checks each high-risk mark live
  with the official USPTO TSDR API, using your own free key. It adds `tsdrLive` to each high-risk mark. This option
  is still in beta. It has not yet been validated against the live TSDR API, and it is rate limited to about 50
  lookups a minute, so it is slow on big batches. If TSDR can't be reached, `tsdrLive` holds an `error` note
  instead. The main check does not depend on it.

#### Input limits and invalid items

- Very long text is truncated, not rejected: titles at 1,000 characters, tags at 200 characters each and 50 tags per
  listing, descriptions at 20,000 characters, phrases at 1,000 characters. Each listing that was cut gets a
  `warnings` field. Marketplace limits are far lower (Etsy: 140-character titles and 13 tags).
- HTML entities such as `&amp;` and `&#39;` are decoded. Emoji and non-Latin text are accepted. USPTO word marks
  are matched on Latin letters and digits, so a listing written only in another script cannot be checked.
- **Invalid items are reported and never charged.** These include empty listings, wrong types (a number or a list
  where a title belongs), listings with no Latin letters or digits, and exact duplicates of an earlier listing or
  phrase. Each one becomes a dataset row with `type: "skipped"`, `charged: false` and a `skipReason`. The rest of
  the batch still runs. If every item is invalid, the run fails with a clear message and nothing is charged.

### Output

Each listing or phrase becomes one dataset item. The Console shows two views:

- **Overview**: one row per listing with its overall risk.
- **Flags**: one row per flagged phrase.

Example (shortened):

```json
{
  "listingId": "etsy-1",
  "title": "Retro Hakuna Matata Shirt, Funny Lion Tee",
  "overallRisk": "high",
  "flaggedPhrases": ["hakuna matata"],
  "flags": [{
    "phrase": "hakuna matata",
    "foundIn": ["tags", "title"],
    "risk": "high",
    "matchType": "exact",
    "reason": "Exact match of a live standard-character (word) mark in a selected class",
    "matchingClasses": ["025"],
    "liveMarksMatched": 2,
    "marks": [{
      "mark": "HAKUNA MATATA",
      "serialNumber": "74558335",
      "registrationNumber": "2700605",
      "status": "REGISTERED",
      "statusDescription": "Registered and renewed",
      "standardCharacters": true,
      "classes": ["025"],
      "owner": "DISNEY ENTERPRISES, INC.",
      "tsdrUrl": "https://tsdr.uspto.gov/#caseNumber=74558335&caseType=SERIAL_NO&searchType=statusSearch"
    }]
  }],
  "indexDataThrough": "2026-09-30",
  "disclaimer": "Automated screening against USPTO live federal marks only. Not legal advice; ..."
}
```

Skipped input items appear as rows with `type: "skipped"` and a `skipReason`. They are not charged.

The run summary is saved to the key-value store under the key `OUTPUT`. It includes counts per risk level, items
checked, skipped items, any items not reached because of your spending limit, and whether the run stopped at
that limit.

### Pricing

Pay per result. No subscription, and no charge for skipped or invalid items.

| What you check | Price | Example cost |
|---|---|---|
| Listing (title plus all tags, every phrase inside) | **$0.01** | 100 Etsy listings: $1.00 |
| Standalone phrase or slogan | **$0.003** | 1,000 slogans: $3.00 |

Use `maxListings` or the run's maximum-cost setting to cap your spend. The Actor stops cleanly when it reaches
your limit.

### Data source and freshness

The index is compiled from the USPTO's **public-domain Trademark bulk data**, using a public mirror of the official
files based on the Trademark Full Text XML annual backfile (TRTYRAP). Where available, USPTO daily files
(TRTDXFAP) are applied on top. Only marks with a live USPTO status are included.

Each result includes `indexDataThrough`, the date the data is current to. **It is currently 2026-09-30.** Updates
from the USPTO daily files are applied periodically, so recent filings, registrations and cancellations may not
appear yet. Check the TSDR link on any flag for the mark's current status.

Checking runs entirely against this local index, so your listings are never sent to any third party. The
optional TSDR check (beta) sends only the serial numbers of flagged marks to the USPTO.

### FAQ

**Is this phrase trademarked?** Put the phrase in `phrases` (or try the
[slogan and quote checker example](https://apify.com/vncntlabs/pod-trademark-checker/examples/slogan-quote-trademark-checker)).
You get every live USPTO mark with that wording in the product classes you sell in, with its owner, status and
TSDR link. Remember this is screening, not legal advice: "none" means no live federal mark was found, not that
the phrase is free to use.

**Is there a trademark checker for Etsy, Redbubble, Merch by Amazon or KDP?** Yes, this is one. The check is
the same for every marketplace, but the right classes differ: clothing is class 25, stickers and books are
class 16, mugs are class 21 and phone cases are class 9. The [examples above](#ready-made-examples-by-platform)
are already set up for each platform.

**Does a "high" flag mean I will get a takedown?** No. It means a live USPTO word mark with the same wording exists
for the same kind of goods. Marketplaces often remove listings when the owner complains, without any court ruling.
Treat **high** as "remove or rename" unless a trademark attorney has advised you otherwise.

**Why is "cat" or "love" not flagged by default?** Single dictionary words are registered for many narrow goods,
which makes them very noisy. They are reported as low risk unless one owner holds many live marks for that exact
word (for example CAT, owned by Caterpillar Inc.) or dominates a less common word (YETI).

**Does it check images or logos?** No, only text.

**Which marketplaces is it for?** Any marketplace: Etsy, Amazon Merch on Demand, KDP covers and titles, Redbubble,
Teepublic, Spreadshirt, Zazzle, Society6, Printful and Printify stores, and Shopify.

# Actor input Schema

## `listings` (type: `array`):

Listings to screen. Each item is either a plain title string or an object {"id": "...", "title": "...", "tags": \["..."], "description": "..."}. Every 1-5 word phrase in the title and in each tag is checked. Charged per listing. Invalid items (empty, wrong type, duplicates) are reported as type "skipped" rows and never charged. Limits: title 1,000 chars, 50 tags of 200 chars (longer input is truncated with a warning).

## `phrases` (type: `array`):

Optional standalone phrases or slogans to check exactly as written (no n-gram splitting), e.g. a design slogan before you print it. Charged per phrase.

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

USPTO international classes to check. Default covers POD products: 025 clothing, 021 mugs/drinkware, 016 stickers/paper/books, 009 phone cases, 014 jewelry, 018 bags, 020 pillows/decor, 024 blankets/textiles, 028 toys/games.

## `inferClassesFromListing` (type: `boolean`):

If a listing names its product (shirt, hoodie, mug, sticker, phone case, tote, pillow, blanket, toy...), only check the matching classes from your list for that listing. Listings without a product word use all selected classes. Recommended: cuts flags from unrelated goods (e.g. a word registered only for software when you sell t-shirts).

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

exact: only phrases identical to a live mark (after normalizing case/punctuation). fuzzy: also spacing variants ("justdoit"), singular/plural, USPTO pseudo-marks and phrases that cover most of a longer mark.

## `includeLowRisk` (type: `boolean`):

Also report common dictionary words and partial matches that happen to be registered (e.g. "cat", "love"). Off keeps the output focused.

## `checkDescription` (type: `boolean`):

Also extract phrases from listing descriptions (slower and noisier).

## `maxListings` (type: `integer`):

Stop after this many listings + phrases (0 = no limit). Use it to cap spend.

## `maxFlagsPerListing` (type: `integer`):

Highest-risk flags are kept first.

## `confirmWithTsdr` (type: `boolean`):

Beta. Optional. Re-checks the status of each high-risk mark in real time with the official USPTO TSDR API and adds a tsdrLive field (or an error note if TSDR is unreachable). Requires your own free TSDR API key. Slow (about 50 lookups/minute); not yet validated against the live API. The main check works without it.

## `tsdrApiKey` (type: `string`):

Your USPTO TSDR API key (only used when the beta TSDR confirmation is on). Stored encrypted.

## Actor input object example

```json
{
  "listings": [
    {
      "id": "etsy-1",
      "title": "Retro Hakuna Matata Shirt, Funny Lion Tee, Vintage Graphic T-Shirt",
      "tags": [
        "hakuna matata",
        "lion shirt",
        "vintage tee",
        "gift for her"
      ]
    },
    {
      "id": "etsy-2",
      "title": "Cute Cat Mom Coffee Mug | Cat Lover Gift",
      "tags": [
        "cat mom",
        "coffee mug",
        "cat lover gift"
      ]
    },
    "Life is Good Camping Hoodie for Outdoor Lovers"
  ],
  "classes": [
    "025",
    "021",
    "016",
    "009",
    "014",
    "018",
    "020",
    "024",
    "028"
  ],
  "inferClassesFromListing": true,
  "matchMode": "fuzzy",
  "includeLowRisk": false,
  "checkDescription": false,
  "maxListings": 0,
  "maxFlagsPerListing": 25,
  "confirmWithTsdr": false
}
```

# Actor output Schema

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

One row per listing or phrase: overallRisk (high/medium/low/none), flags with matching live USPTO marks. Invalid or duplicate inputs appear as type=skipped rows (not charged).

## `flags` (type: `string`):

One row per flagged phrase (dataset view 'flags').

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

Counts per risk level, skipped items, items not reached at the spending limit, index data date.

# 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 = {
    "listings": [
        {
            "id": "etsy-1",
            "title": "Retro Hakuna Matata Shirt, Funny Lion Tee, Vintage Graphic T-Shirt",
            "tags": [
                "hakuna matata",
                "lion shirt",
                "vintage tee",
                "gift for her"
            ]
        },
        {
            "id": "etsy-2",
            "title": "Cute Cat Mom Coffee Mug | Cat Lover Gift",
            "tags": [
                "cat mom",
                "coffee mug",
                "cat lover gift"
            ]
        },
        "Life is Good Camping Hoodie for Outdoor Lovers"
    ],
    "classes": [
        "025",
        "021",
        "016",
        "009",
        "014",
        "018",
        "020",
        "024",
        "028"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("vncntlabs/pod-trademark-checker").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 = {
    "listings": [
        {
            "id": "etsy-1",
            "title": "Retro Hakuna Matata Shirt, Funny Lion Tee, Vintage Graphic T-Shirt",
            "tags": [
                "hakuna matata",
                "lion shirt",
                "vintage tee",
                "gift for her",
            ],
        },
        {
            "id": "etsy-2",
            "title": "Cute Cat Mom Coffee Mug | Cat Lover Gift",
            "tags": [
                "cat mom",
                "coffee mug",
                "cat lover gift",
            ],
        },
        "Life is Good Camping Hoodie for Outdoor Lovers",
    ],
    "classes": [
        "025",
        "021",
        "016",
        "009",
        "014",
        "018",
        "020",
        "024",
        "028",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("vncntlabs/pod-trademark-checker").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 '{
  "listings": [
    {
      "id": "etsy-1",
      "title": "Retro Hakuna Matata Shirt, Funny Lion Tee, Vintage Graphic T-Shirt",
      "tags": [
        "hakuna matata",
        "lion shirt",
        "vintage tee",
        "gift for her"
      ]
    },
    {
      "id": "etsy-2",
      "title": "Cute Cat Mom Coffee Mug | Cat Lover Gift",
      "tags": [
        "cat mom",
        "coffee mug",
        "cat lover gift"
      ]
    },
    "Life is Good Camping Hoodie for Outdoor Lovers"
  ],
  "classes": [
    "025",
    "021",
    "016",
    "009",
    "014",
    "018",
    "020",
    "024",
    "028"
  ]
}' |
apify call vncntlabs/pod-trademark-checker --silent --output-dataset

```

## MCP server setup

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

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/BA95fL3SPC4LYLivL/builds/gFf4960qQOW5ZP0OM/openapi.json
