The USPTO received over 612,000 trademark applications in 2025. Roughly one in three faced an office action, often for likelihood of confusion with an existing mark. Each filing costs $250 to $350 per class, and that's the cheap part. Rebranding after launch (new domain, new packaging, new marketing collateral) can cost tens of thousands.
A trademark name checker catches these conflicts before you file. Most free tools only do exact-match lookups, which miss the variations that actually trigger office actions: phonetic similarities, misspellings, compound-word variants. This tutorial builds a checker that runs exact, phonetic, and fuzzy search in a single API call across five trademark offices.
Why Checking Brand Name Availability Matters
A trademark is a word, phrase, symbol, or design that identifies the source of goods or services. When two marks are confusingly similar in the same product category, the earlier registration wins. The applicant who filed second gets an office action, a refusal, or (worse) a cease-and-desist after they've already gone to market.
The risk is not hypothetical. If you need to check if a business name is trademarked before filing, waiting until after the application is the wrong time to find out. With over 1,700 new filings hitting the USPTO every day, the probability of conflict grows with every application. And the cost of missing a conflict compounds at each stage. Catching it before filing costs you an API call. Catching it during examination costs you legal fees and a delayed launch. Catching it after launch costs you a rebrand.
Most free trademark search tools check for exact matches only. If you're evaluating the name "ASTRALUX," an exact-match search will find "ASTRALUX" but miss "ASTRA LUX," "ASTROLUX," and "ASTRO LUXE," all of which could trigger a likelihood-of-confusion refusal. Real clearance requires phonetic matching (would these sound the same to a consumer?) and fuzzy matching (are these close enough to confuse?).
The filtering dimension matters too. Nice classes are the international system for categorizing goods and services into 45 categories. "DOVE" is registered for both soap (Class 3) and chocolate (Class 30) by different companies because the products don't compete. A trademark name checker that ignores class context will flood you with false positives.
What You'll Build
By the end of this tutorial, you'll have a TypeScript script that runs a brand name availability check against five trademark offices in a single API call. The script uses multi-strategy search (exact + phonetic + fuzzy combined) and returns potential conflicts with similarity scores, Nice class, registration status, office of origin, and owner.
The data coverage behind the API:
- USPTO: 10M+ records (the largest single-office dataset)
- EUIPO: 2M+ records covering the European Union
- WIPO Madrid: 1.5M international registrations
- CIPO: 900K+ Canadian records
- IPOS: 400K+ Singapore records
Trademark Records by Office
If you've used legacy tools like TESS (now retired) or the WIPO Global Brand Database, this is a different experience. One API call, multiple strategies, five offices, structured JSON response.
Setup
Prerequisites: Node.js 18+ and a Signa API key (sign up at signa.so).
Install the SDK:
npm install @signa/sdk
Initialize the client:
import Signa from '@signa/sdk';
const signa = new Signa({ apiKey: 'sig_live_...' });
For curl requests, pass the key in the Authorization header:
curl https://api.signa.so/v1/trademarks/search \
-H "Authorization: Bearer sig_live_..."
That's it. Signa provides a trademark name search API with no OAuth flows, no token refresh. One key, one header.
Building the Trademark Name Checker
The core search call
The search endpoint accepts a mark object with the text to search and an array of strategies. Combining strategies in a single request means you get exact matches, phonetic matches, and fuzzy matches without making three separate calls.
curl:
curl -X POST https://api.signa.so/v1/trademarks/search \
-H "Authorization: Bearer sig_live_..." \
-H "Content-Type: application/json" \
-d '{
"mark": {
"text": "ASTRALUX",
"strategies": ["exact", "phonetic", "fuzzy"]
}
}'
TypeScript SDK:
const results = await signa.trademarks.search({
mark: {
text: 'ASTRALUX',
strategies: ['exact', 'phonetic', 'fuzzy']
}
});
Reading the response
The API returns a paginated list following Stripe-style conventions. The top-level response wraps results in a data array with cursor pagination:
{
"data": [
{
"id": "tm_8Kx2mPqR4vNw",
"mark_text": "ASTROLUX",
"office": "USPTO",
"serial_number": "97482613",
"filing_date": "2024-03-15",
"status": "registered",
"nice_classes": [9, 11],
"owner": {
"id": "own_3Jn7kLpQ9wXy",
"name": "Astrolux Technologies Inc."
},
"similarity": {
"strategy": "phonetic",
"score": 0.91
}
},
{
"id": "tm_5Fw9nRtY2mKp",
"mark_text": "ASTRA LUX",
"office": "EUIPO",
"serial_number": "018934721",
"filing_date": "2023-11-08",
"status": "registered",
"nice_classes": [9],
"owner": {
"id": "own_8Qm4vXwR6nLp",
"name": "Astra Lux GmbH"
},
"similarity": {
"strategy": "fuzzy",
"score": 0.87
}
},
{
"id": "tm_2Rw6pKtN8mXq",
"mark_text": "ASTRALUX",
"office": "CIPO",
"serial_number": "2198453",
"filing_date": "2025-01-22",
"status": "pending",
"nice_classes": [35],
"owner": {
"id": "own_6Lp3nXwK9mRq",
"name": "Astralux Marketing Ltd."
},
"similarity": {
"strategy": "exact",
"score": 1.0
}
}
],
"has_more": false,
"next_cursor": null
}
A few things to note in this response:
- Prefixed IDs (
tm_,own_): every resource type has a distinct prefix, so you can identify what an ID refers to at a glance. similarity.strategytells you which search strategy matched this result. A score of 0.91 on phonetic means the mark sounds very similar. A score of 1.0 on exact means it's the same string.nice_classesis an array because marks can cover multiple classes. The ASTROLUX registration above covers both Class 9 (software/electronics) and Class 11 (lighting), which is relevant if your product falls in either category.statusdistinguishes live registrations from pending applications, abandoned filings, and cancelled marks.
When has_more is true, pass next_cursor as a parameter to fetch the next page. The SDK handles this automatically with async iteration.
Filtering by Nice class
Without class filtering, a search for "ASTRALUX" returns every match across all 45 Nice classes. If you're building a software product (Class 9), a match in Class 30 (food products) is noise.
For more on how Nice classes work and which class fits your product, see the Nice classification guide for developers.
Add a nice_classes filter to scope results:
const results = await signa.trademarks.search({
mark: {
text: 'ASTRALUX',
strategies: ['exact', 'phonetic', 'fuzzy']
},
nice_classes: [9, 42]
});
This returns only marks registered in Class 9 (software, electronics) or Class 42 (SaaS, cloud services). The same "DOVE" can coexist across unrelated classes, and your checker should reflect that distinction.
Don't know your Nice class? Let the API suggest it
Most developers don't have Nice classes memorized. Signa's classification suggest endpoint takes a plain-language product description and returns the relevant classes, ranked by confidence:
curl -X POST https://api.signa.so/v1/classifications/suggest \
-H "Authorization: Bearer sig_live_..." \
-H "Content-Type: application/json" \
-d '{"description": "cloud-based project management software for engineering teams"}'
const suggestion = await signa.classifications.suggest({
description: 'cloud-based project management software for engineering teams'
});
The response ranks classes with a confidence level and a rationale:
{
"object": "classification_suggestion",
"query": "cloud-based project management software for engineering teams",
"classes": [
{
"class_number": 42,
"title": "Scientific and technological services",
"confidence": "high",
"rank": 1,
"recommendation_type": "core",
"rationale": "SaaS and cloud-based software platforms fall under class 42."
},
{
"class_number": 9,
"title": "Software and computer hardware",
"confidence": "high",
"rank": 2,
"recommendation_type": "core",
"rationale": "Downloadable or installed software components fall under class 9."
}
],
"ambiguous": false,
"clarification_question": null
}
This lets you chain the two calls: suggest classes from a product description, then pass those classes into the trademark search. The complete checker below supports both explicit classes and description-based lookup.
The complete script
Here's the full checker as a CLI tool. It takes a brand name and either explicit Nice classes or a product description for automatic class suggestion:
import Signa from '@signa/sdk';
const signa = new Signa({ apiKey: process.env.SIGNA_API_KEY! });
async function suggestClasses(description: string): Promise<number[]> {
const suggestion = await signa.classifications.suggest({ description });
const relevant = suggestion.classes.filter(
(c) => c.confidence === 'high' || c.confidence === 'medium'
);
console.log('Suggested classes:');
for (const c of relevant) {
console.log(` Class ${c.class_number}: ${c.title} (${c.confidence})`);
}
console.log();
return relevant.map((c) => c.class_number);
}
async function checkAvailability(
name: string,
niceClasses?: number[],
description?: string
) {
if (description && !niceClasses?.length) {
niceClasses = await suggestClasses(description);
}
const params: any = {
mark: {
text: name,
strategies: ['exact', 'phonetic', 'fuzzy']
}
};
if (niceClasses?.length) {
params.nice_classes = niceClasses;
}
const results = await signa.trademarks.search(params);
if (!results.data.length) {
console.log(`No conflicts found for "${name}"`);
return;
}
console.log(`Found ${results.data.length} potential conflicts for "${name}":\n`);
for (const tm of results.data) {
console.log(` ${tm.mark_text} (${tm.office})`);
console.log(` Strategy: ${tm.similarity.strategy} | Score: ${tm.similarity.score}`);
console.log(` Status: ${tm.status} | Classes: ${tm.nice_classes.join(', ')}`);
console.log(` Owner: ${tm.owner.name}`);
console.log(` ID: ${tm.id}\n`);
}
}
const name = process.argv[2];
const descriptionOrClasses = process.argv[3];
if (!name) {
console.error('Usage: npx tsx check.ts <brand-name> [nice-classes | product-description]');
process.exit(1);
}
const isClasses = descriptionOrClasses && /^[\d,]+$/.test(descriptionOrClasses);
checkAvailability(
name,
isClasses ? descriptionOrClasses.split(',').map(Number) : undefined,
isClasses ? undefined : descriptionOrClasses
);
Run it with explicit classes:
SIGNA_API_KEY=sig_live_... npx tsx check.ts "ASTRALUX" 9,42
Or with a product description to auto-suggest classes:
SIGNA_API_KEY=sig_live_... npx tsx check.ts "ASTRALUX" "cloud project management software"
The output for our ASTRALUX example:
Found 3 potential conflicts for "ASTRALUX":
ASTROLUX (USPTO)
Strategy: phonetic | Score: 0.91
Status: registered | Classes: 9, 11
Owner: Astrolux Technologies Inc.
ID: tm_8Kx2mPqR4vNw
ASTRA LUX (EUIPO)
Strategy: fuzzy | Score: 0.87
Status: registered | Classes: 9
Owner: Astra Lux GmbH
ID: tm_5Fw9nRtY2mKp
ASTRALUX (CIPO)
Strategy: exact | Score: 1.0
Status: pending | Classes: 35
Owner: Astralux Marketing Ltd.
ID: tm_2Rw6pKtN8mXq
Three results, three different risk levels. The phonetic match "ASTROLUX" in Class 9 at the USPTO is the highest risk for a software product. The exact match in CIPO Class 35 (advertising/marketing services) may or may not be relevant depending on your product scope. The fuzzy match in EUIPO Class 9 is another red flag for the European market.
Handling Edge Cases
Multi-word and compound marks
Brand names that combine words create ambiguity. "BLUE BIRD," "BLUEBIRD," and "BLU BERD" are all potential conflicts with each other, but exact-match search only catches one. The phonetic strategy handles "BLU BERD" (sounds like "BLUEBIRD"), while fuzzy matching catches the spacing and concatenation variants.
This is where multi-strategy search earns its value. A single API call with ["exact", "phonetic", "fuzzy"] catches all three variations without requiring you to generate permutations yourself.
International characters and Unicode
Signa's search uses ICU normalization, which means accented characters, ligatures, and other Unicode variations are handled consistently. A search for "CAFE" will match "CAFÉ," and a search for "MULLER" will match "MÜLLER." You don't need to strip diacritics or normalize input yourself.
Office-scoped search
If you only care about US and EU markets, filter to those offices:
const results = await signa.trademarks.search({
mark: {
text: 'ASTRALUX',
strategies: ['exact', 'phonetic', 'fuzzy']
},
offices: ['USPTO', 'EUIPO']
});
curl -X POST https://api.signa.so/v1/trademarks/search \
-H "Authorization: Bearer sig_live_..." \
-H "Content-Type: application/json" \
-d '{
"mark": {
"text": "ASTRALUX",
"strategies": ["exact", "phonetic", "fuzzy"]
},
"offices": ["USPTO", "EUIPO"]
}'
This reduces noise from jurisdictions you don't plan to operate in.
Live vs. dead marks: why abandoned marks still matter
The status field distinguishes active registrations from abandoned or cancelled marks. Your first instinct might be to filter these out:
const results = await signa.trademarks.search({
mark: {
text: 'ASTRALUX',
strategies: ['exact', 'phonetic', 'fuzzy']
},
status: ['registered', 'pending']
});
But consider keeping dead marks visible. An abandoned registration doesn't guarantee the name is available. The prior owner may still have common-law rights from ongoing use. A cancelled mark might be in the process of being re-filed. And a USPTO examiner may still cite a dead registration in an office action if the abandonment is recent.
Display dead marks with lower priority, but don't hide them entirely.
From Checker to Clearance Workflow
This tutorial builds a screening tool, not a legal opinion. A trademark name checker identifies potential conflicts. It doesn't evaluate the strength of those conflicts, assess the likelihood of confusion under the DuPont factors, or predict how an examiner will rule. Consult a trademark attorney for legal guidance specific to your situation.
That said, the checker is a useful building block. A few places it fits:
- Onboarding flows. If your product involves brand creation (e-commerce platforms, domain registrars, logo makers), embed a trademark check into the naming step. Flag conflicts before the user commits to a name.
- Batch screening. Run a list of brand name candidates through the checker and rank them by conflict count and similarity score. The name with zero matches across your target classes and offices is the strongest candidate.
- Brand naming tools. Combine name generation with trademark screening. Generate candidates, check each one, surface only the clear options.
The natural next step after screening is ongoing monitoring: watching for new filings that might conflict with a name you've already adopted. Signa's API is building toward that workflow.
Sign up for a free API key at signa.so and build your first trademark search in under five minutes.
