Print-on-demand sellers, Etsy shops and brand namers all hit the same question: has someone already trademarked this phrase? The USPTO's own Trademark Search answers it one query at a time, which is fine for a single name and painful for a spreadsheet of 300 slogans.
The other difficulty is reading the results. A phrase can match dead marks that no longer matter, live marks in classes unrelated to your product, or a pending application that has not been registered yet but can still block you. What you want is a quick triage: which phrases are obviously taken for your product type, which need a human look, and which have nothing on the register.
This guide shows how to do that triage in bulk against the official US register, and where its limits are. It is a first-pass filter, not legal advice, and it does not replace a trademark attorney for anything you plan to build a brand on.
Step by step
Be clear about what a register check can tell you
The check reports what is on the USPTO register at the moment you run it. It does not cover unregistered common-law rights, state registrations, marks in other countries, design-only marks with no matching text, or confusingly similar marks such as a different spelling. Treat a clear verdict as 'nothing found on the register', not 'free to use'.
Prepare your phrase list
Put one phrase, slogan or brand name per entry, up to 10,000 per run. Case and extra spaces do not matter, a trailing . or ! is ignored in exact mode, and duplicates are removed and never charged, so you can paste straight from a design sheet without cleaning it first.
Pick a match mode
exact only finds marks whose whole text equals your phrase. contains finds your phrase anywhere inside a mark, so CAT LOVER also finds CRAZY CAT LOVERS. words requires every word of the phrase in any order, with very common words such as IT or THE ignored by the USPTO index. For screening designs, contains is the cautious default.
Limit the check to your product's classes
Trademarks are registered for specific Nice classes. For apparel use 25, for mugs and housewares 21, for stickers, posters and paper goods 16, for bags 18 and for phone cases 9. The class filter applies to the verdict too, so a phrase registered only for software comes back clear when you check classes 25 and 21. Leave classes empty if you want to see every registration.
{ "phrases": ["Mama Bear", "Coffee Before Talkie", "Plant Mom Club"], "searchMode": "contains", "classes": ["25"], "statusFilter": "all", "maxResultsPerPhrase": 10 }Triage the results by verdict
Each row has a verdict of registered-live, pending, dead-only or clear, plus match counts. Pending still counts as a risk: it is a live application in examination. The script below sorts a run's results into three CSV files: blocked, needs review and nothing found. It lists the first matching owner and the TSDR link so the review pile is quick to work through.
import csv import os from apify_client import ApifyClient client = ApifyClient(os.environ["APIFY_TOKEN"]) phrases = [line.strip() for line in open("phrases.txt", encoding="utf-8") if line.strip()] run = client.actor("actor-forge/uspto-trademark-search").call(run_input={ "phrases": phrases, "searchMode": "contains", "classes": ["25", "21"], "maxResultsPerPhrase": 5, }) buckets = {"blocked": [], "review": [], "nothing_found": []} for row in client.dataset(run["defaultDatasetId"]).iterate_items(): verdict = row.get("verdict") matches = row.get("matches") or [] top = matches[0] if matches else {} record = [row.get("phrase"), verdict, top.get("mark", ""), top.get("owner", ""), top.get("tsdrUrl", "")] if verdict == "registered-live": buckets["blocked"].append(record) elif verdict in ("pending", "dead-only"): buckets["review"].append(record) elif verdict == "clear": buckets["nothing_found"].append(record) for name, rows in buckets.items(): with open(f"{name}.csv", "w", newline="", encoding="utf-8") as f: writer = csv.writer(f) writer.writerow(["phrase", "verdict", "mark", "owner", "tsdrUrl"]) writer.writerows(rows) print(name, len(rows))Open the official record for anything borderline
Every listed mark has a TSDR link to the official USPTO case file, along with the owner name, state and country, international classes, goods and services, status text and filing and registration dates. Reading the goods and services description is usually the fastest way to see whether a live mark actually covers what you sell.
Re-check before you reprint or scale
The register changes as new applications are filed; the data is usually updated daily. Re-run your active phrases before a big reprint or a new marketplace launch, and talk to a trademark attorney before relying on any result for a brand you plan to invest in.
Sample input
{
"phrases": [
"Just Do It",
"Mama Bear",
"Webvan",
"Coffee Before Talkie"
],
"searchMode": "exact",
"statusFilter": "live",
"maxResultsPerPhrase": 20
}Sample output
Each phrase gets one row with its verdict and live, registered, pending and dead match counts:
| phrase | verdict | liveMatchCount | registeredLiveCount | pendingCount | deadMatchCount | matches[0].owner | matches[0].internationalClasses |
|---|---|---|---|---|---|---|---|
| JUST DO IT | registered-live | 12 | 7 | 5 | 9 | Nike, Inc. | 009, 010, 021, 025 |
| MAMA BEAR | registered-live | 8 | 8 | 0 | 26 | Build-A-Bear Retail Management, Inc. | 028 |
| WEBVAN | dead-only | 0 | 0 | 0 | 13 | ||
| COFFEE BEFORE TALKIE | clear | 0 | 0 | 0 | 0 |
Run it from code
cURL
curl -X POST "https://api.apify.com/v2/acts/actor-forge~uspto-trademark-search/run-sync-get-dataset-items" \
-H "Authorization: Bearer $APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"phrases": ["Just Do It", "Mama Bear", "Webvan", "Coffee Before Talkie"], "searchMode": "exact", "statusFilter": "live", "maxResultsPerPhrase": 20}'JavaScript (Node.js)
// Node.js 18+ (built-in fetch). Set APIFY_TOKEN in your environment.
const input = {
"phrases": [
"Just Do It",
"Mama Bear",
"Webvan",
"Coffee Before Talkie"
],
"searchMode": "exact",
"statusFilter": "live",
"maxResultsPerPhrase": 20
};
const res = await fetch(
"https://api.apify.com/v2/acts/actor-forge~uspto-trademark-search/run-sync-get-dataset-items",
{
method: "POST",
headers: {
Authorization: `Bearer ${process.env.APIFY_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify(input),
}
);
const items = await res.json();
console.log(items.length, items[0]);Python
# pip install apify-client (set APIFY_TOKEN in your environment)
import os
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run_input = {
"phrases": [
"Just Do It",
"Mama Bear",
"Webvan",
"Coffee Before Talkie"
],
"searchMode": "exact",
"statusFilter": "live",
"maxResultsPerPhrase": 20
}
run = client.actor("actor-forge/uspto-trademark-search").call(run_input=run_input)
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
print(item)The run-sync-get-dataset-items endpoint waits for the run (up to 5 minutes) and returns the dataset rows directly. For longer runs, start the run asynchronously and read the dataset when it finishes, as the Python example does. Replace APIFY_TOKEN with your own token from the Apify console.
What it costs
Checking a batch of 250 slogans costs 250 x $0.03 = $7.50, and 1,000 phrases cost $30.00. Duplicates, invalid phrases and failed checks are not charged.
| Charged for | When | Price | Per 1,000 | Worked example |
|---|---|---|---|---|
| Phrase checked | Charged per phrase checked against the register and delivered (verdict, counts and up to 100 matching marks included) | $0.03 | $30 | 1,000 phrases = $30.00 |
| Actor Start (platform start fee) | Charged when the Actor starts running. Number of events charged depends on Actor memory (one event per GB, minimum one event). | $0.00005 | — | One run at up to 1 GB = $0.00005 (charged as one event) |
Ready to try it? USPTO Trademark Search — Check If a Phrase Is Trademarked runs on Apify, pay per result.
Run on Apify → Read the full actor pageTips and pitfalls
- Use contains rather than exact when screening, because a longer registered mark that includes your phrase can still be a problem.
- statusFilter only changes which marks are listed in matches; the verdict and counts always cover every status.
- Set maxResultsPerPhrase to 0 if you only want verdicts and counts; the price is the same either way.
- The actor checks about 7,000 phrases per hour. For lists over about 6,000 phrases, raise the run timeout above the default one hour.
- Owner street addresses, emails and phone numbers are never returned, only the owner's name, entity type and state or country.
FAQ
Can I put a trademarked phrase on a T-shirt if the mark is in a different class?
The class filter shows whether a mark is registered for your product class, but whether a use infringes is a legal question that depends on more than the class. Treat the result as information and ask a trademark attorney if it matters.
What does dead-only mean?
Only abandoned, cancelled or expired marks match the phrase. There is no live registration or pending application for it on the register, though older dead marks can be worth a look.
Does this check trademarks outside the US?
No. It checks only the US register held by the USPTO. Marks in other countries and EU registrations are not covered.
Is the data official?
It comes from the public backend of USPTO Trademark Search (tmsearch.uspto.gov) and is as fresh as that service, usually updated daily. The actor is not affiliated with the USPTO.
Actors used in this guide
USPTO Trademark Search — Check If a Phrase Is Trademarked
Bulk-check phrases against the official US trademark register before you list a T-shirt, mug or Amazon product. Verdict per phrase (registered…
$30 per 1,000 · phrase checkedRelated guides
How to Export Shopify Store Product Data to CSV or JSON
Give the ActorForge Shopify Store Scraper a store URL, let it page through the public products.json feed, then download the dataset as CSV or JSON…
How to Track Google Trends Trending Searches by Country
Run the ActorForge Google Trends actor with a list of country codes, schedule it daily with only-new mode, and keep a small pandas history to see…