Brand Name IP Screener
Pricing
from $1,000.00 / 1,000 name screened in one jurisdictions
Brand Name IP Screener
Screens brand names vs curated famous-mark seed datasets (not full registry) for trademark-collision risk across FR/US/EU/WO/UK — visual + phonetic similarity scoped to Nice classes, with a domain co-check. Clear/caution/high-risk per jurisdiction. Informational only — not legal advice.
Pricing
from $1,000.00 / 1,000 name screened in one jurisdictions
Rating
0.0
(0)
Developer
Protocol
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
7 days ago
Last modified
Categories
Share
Generate brand-name candidates from a positioning brief, or screen names you already have, for
trademark-collision risk — with France (INPI) and the EU (EUIPO) covered first, plus the
US (USPTO), UK (UKIPO), and international (WIPO) — then get a domain-availability check and a
collision-risk verdict in one run — clear (no collision found among the marks screened),
caution, or high-risk. Each jurisdiction is screened against a curated famous-mark seed
dataset, not full registry coverage — this is a pre-filing risk triage, not a substitute for a
full-registry clearance search. Unlike a domain checker (which only
asks "is the URL free?"), this asks the harder question a trademark examiner asks: is this name
confusingly similar — including phonetically (sounds-alike) — to an existing registered mark in my
class of goods/services?
- Generate + screen + rank in one run — not just an LLM prompt, not just a trademark search.
- 5 jurisdictions: France (INPI) and the EU (EUIPO) first, then US (USPTO), UK (UKIPO), and International (WIPO) — each screened against a curated dataset of well-known, real registered marks.
- Visual + phonetic similarity, the way examiners assess likelihood of confusion — not just
spelling.
Teslaais correctly flagged againstTeslaeven though it isn't a substring match. - Domain co-check — probes
.com,.io,.ai,.devvia RDAP in the same run. - Agent-ready: flat CSV-friendly dataset plus a structured
OUTPUTrecord per name, each carrying a mandatory "not legal advice" disclaimer.
Informational screening only — not legal advice. Registry-free ≠ trademark-free ≠ right-to-use. Clearance requires a qualified trademark attorney. This disclaimer ships on every result.
Quick start
- Create a free Apify account (or sign in).
- Open this Actor's page and go to the Input tab.
- Either add at least one name under
names(screen mode), or fill inpositioning(generate mode) — exactly one of the two is required. - Click Start and wait a few seconds.
- Open the Storage tab to view the per-jurisdiction dataset, or read the
OUTPUTkey-value record for the full structured verdict per name.
What this Actor does
Screen mode (names set):
- Normalizes each name (lowercase, diacritics folded, legal suffixes stripped) to its distinctive core.
- Screens it against every selected jurisdiction's curated mark dataset, scoring visual
similarity (Damerau-Levenshtein) and phonetic similarity (Double Metaphone, English + French)
in parallel and fusing the two into a 0–100
ipRiskScore. - Scopes to Nice classes — your goods/services classes 1–45, either supplied directly or
inferred from a free-text
industryfield. - Co-checks domain availability via RDAP across 5 TLDs (optional, on by default).
- Writes a flat dataset (one row per name × jurisdiction) plus a structured
OUTPUTrecord per name with an overallbrandReadinessverdict and a human-readable recommendation.
Generate mode (positioning set instead of names) does all of the above, plus runs it as a
closed loop:
- Generates candidate names from your positioning brief via an LLM (DeepSeek primary, GPT-4o-mini fallback via OpenRouter).
- Filters them deterministically — profanity, banned words, duplicates, and your
mustInclude/avoidconstraints (enforced after generation, not just asked of the model) — entirely locally, zero network calls for this stage. - Screens every survivor through the exact same pipeline as screen mode above.
- Ranks them by a composite score: naming quality × (1 − ipRisk) × domain availability — so the names at the top of the list are good and show low collision risk in the screened seed and are actually available.
Why use it
- Catch what a spelling check misses. Phonetic matching flags sounds-alike collisions
(
BrandcraftvsBrandkraft) that a substring or Levenshtein-only check would pass clean. - Don't waste a clearance search on a taken domain. The RDAP co-check runs in the same pass.
- Skip the false sense of security. Jurisdictions with no enabled data source report
not_screened, and everyclearcarries amarksScreenedcount so a weakclearcan't pass for a strong one — this Actor never returns a falseclear. - Generate names that are actually usable, not just names that sound good — generate mode ranks by ownability, not just LLM creativity.
How this compares
This Actor screens brand names against a curated seed of well-known, real registered marks per
jurisdiction (FR/US/EU/WO/UK), scores visual + phonetic similarity scoped to your Nice classes,
co-checks domain availability via RDAP, and returns a clear / caution / high-risk verdict in
seconds. It is informational screening, not legal advice — read every verdict as "no collision
found among the marks screened," never as "trademark-free" or "safe to launch."
| Option | What you get | Cost / speed | Honest limits |
|---|---|---|---|
| This Actor | Automated, scored screening across 5 jurisdictions, visual + phonetic, Nice-class-scoped, plus RDAP domain co-check, in one run. | Seconds; ~$1/name×jurisdiction screened (generate mode is free). | Curated famous-mark seed, not the full registry. "No collision found" means among the marks screened — a real register may hold a conflicting mark outside the sample. Informational only; not a clearance opinion. |
| $150–$300 paralegal / trademark-attorney clearance | An authoritative, jurisdictionally complete clearance opinion from a qualified attorney who searches the full register, weighs likelihood of confusion in light of case law, and advises on filing strategy. | $150–$300+; hours to days. | Slower and more expensive. Authoritative where this Actor is probabilistic — the right step before you file or invest. |
| Manual searching of EUIPO eSearch Plus / TMview / USPTO / INPI | Free, direct access to the live registries. | Free; slow — minutes-to-hours per name per jurisdiction, and you do all the scoring yourself. | Un-scored: you eyeball similarity and easily miss visual/phonetic near-misses (Teslaa vs Tesla, Brandkraft vs Brandcraft). No Nice-class scoping unless you filter by hand. No domain co-check. Easy to conclude "no match found" when a sounds-alike collision is one tab over. |
| Doing nothing (blind launch) | Nothing. | $0 upfront. | Highest downstream risk: a cease-and-desist, opposition, or rebrand after you've already invested in marketing, inventory, or filings. |
When to pick what
- Use this Actor first to triage a long list cheaply — drop obvious collisions, rank what's left,
and find out which names are even worth paying a human to clear. A
high-riskverdict here is a strong signal to drop or rename; aclearis "no collision found in the curated seed," not a green light. - Engage a qualified trademark attorney before you file, invest, or launch — especially for any
name this Actor scored
cautionorhigh-risk, and for any name you can't afford to rebrand later. The attorney's clearance is the authoritative step this Actor deliberately does not replace (see theattorneyReviewflag on every result). - Manual registry searching is free and worth doing as a second opinion on a shortlisted name, but it doesn't replace scored similarity (you'll miss phonetic near-misses) and it doesn't scale past a name or two.
- Doing nothing is the only option that costs nothing upfront and exposes you to the full cost of a collision discovered after launch.
Informational screening only — not legal advice. "Low collision risk found in the curated seed" and "no collision found among the marks screened" are probabilistic observations from a famous-mark sample, not a guarantee of right-to-use. Clearance requires a qualified trademark attorney.
Complete the funnel
A name that screens with low collision risk still needs handles. Once this Actor ranks your
candidates, check whether the matching usernames are free across GitHub, X, Instagram, Threads,
TikTok, Twitch, and YouTube with the sibling AI Username Generator & Availability
Checker Actor
(Protocol/username-generator-checker) — the next step in the same naming workflow. Generate
there, or validate a name you already have, and finish the
name → trademark screen → handle + domain check loop in two paid runs.
Same "not legal advice" boundary: handle availability is a separate signal from trademark right-to-use. Both still require a qualified attorney's clearance before you file.
Pricing
This Actor is billed on pay-per-event (PPE) — you pay for value milestones, not compute time. There is no rental fee and no subscription; you only pay for the billable events below.
| Event | When it fires | Price |
|---|---|---|
screening_report_produced | Screen mode — one name fully screened across all your selected jurisdictions, with the structured OUTPUT verdict written. Billed per name × jurisdiction that returns screened (i.e. a real collision check ran). | $1.00 |
candidate_generated_and_rescreened | Generate mode — one candidate that survived the deterministic filter (profanity / banned-word / duplicate) and was screened through the full IP + RDAP pipeline. | $0.00 |
What you never pay for:
- Generate mode — it is free. Name generation and the full IP + RDAP screening of every generated candidate cost nothing, in any number of jurisdictions (see below).
- Jurisdiction rows that report
not_screened(no enabled data source) — never billed, never a falseclear. - Candidates in generate mode that the Tier-1 filter rejected — they're listed under
OUTPUT.filteredat no charge. - Compute time, retries, or LLM fallback attempts that don't produce a result.
Worked examples (launch pricing, set in the Apify Console; 14 days' notice is given before any change, max once per month):
- Screen 3 names across
["FR", "US", "EU"]→ 3 × 3 = 9 screened rows → $9.00. - Generate mode,
count: 10, 2 candidates filtered out → 8 candidates generated + rescreened → $0.00. - Screen 1 name across 5 jurisdictions where
UKhas no enabled source → 4 screened rows + 1not_screened→ billed for 4 → $4.00.
Generate mode is free.
candidate_generated_and_rescreenedis priced at $0.00, no matter how many candidates you generate or how many jurisdictions you screen them across. Generate-mode runs supply their own LLM key (or provider subscription) viallmApiKey, so this Actor carries no inference cost to pass on. The event still fires at $0 so generate-mode usage stays visible in your run's charge breakdown. Pricing may change in a future version (14 days' notice, as above).
Clearance-grade screening at a fraction of a $200+ paralegal search. Pricing is indicative at launch and finalized in the Console before publication.
Set "Maximum cost per run" high enough for the whole batch. Screen mode costs
$1.00 × names × jurisdictions, so 2 names × 5 jurisdictions is $10.00. If the run's cost cap is reached mid-batch, the Actor stops screening the remaining names and reports them asnot_screenedwith reasonbudget_exhausted— it never silently returns aclearit did not verify, and the unscreened names are never charged. That is the safe failure mode, but it does mean a cap set below the batch total gives you a partial report. Either raise the cap (or leave it unlimited) or split the batch.
Supported jurisdictions
| Jurisdiction | Registry | Coverage |
|---|---|---|
FR | INPI (France) | Curated seed dataset of 163 well-known, real registered marks (software/SaaS, e-commerce, finance, food/beverage, fashion, cosmetics, automotive, media, telecom, transport, pharma, furniture, education, real estate, travel) |
US | USPTO | Curated seed dataset of 203 well-known, real registered marks (tech, fintech, retail, food/beverage, telecom, pharma, automotive, media, hospitality, education) |
EU | EUIPO | Curated seed dataset of 201 well-known, real registered marks (EU-headquartered companies + global brands' EU Trade Mark registrations across tech, automotive, luxury/fashion, food/beverage, finance, pharma, retail, travel, media) |
WO | WIPO (Madrid System) | Curated seed dataset of 321 well-known marks, mostly Asia-headquartered multinationals (electronics, automotive, retail, internet services, finance, industrial) |
UK | UKIPO | Curated seed dataset of 300 well-known, real registered marks (banking/finance, retail/fashion, telecom/media, travel/hospitality, tech, energy/industrial, pharma) |
"Curated seed dataset" means famous-mark collision screening, not full registry coverage. Each registry holds millions of marks; this Actor ships a hand-picked sample of well-known ones per jurisdiction, real and currently registered. Full bulk ingestion (INPI ~3.1 GB on data.gouv.fr; USPTO bulk XML/TSV since TSDR is status-by-number only; EUIPO eSearch Plus OAuth2; WIPO Global Brand Database; UKIPO bulk data / TMview) is a larger, separate undertaking. A jurisdiction in
jurisdictionswith no enabled source reportsscreeningStatus: "not_screened"— never a falseclear.
Input
Exactly one of names / positioning is required.
| Field | Type | Default | Description |
|---|---|---|---|
names | string[] | — | Proposed names to screen (≤20). Leave empty and set positioning instead to generate names first. |
jurisdictions | string[] | ["FR"] | Any of FR, US, EU, WO, UK. |
niceClasses | string[] | [] | Your goods/services classes 1–45. Recommended — scoping removes noise. Leave empty (with industry set) to auto-infer. |
industry | string | — | Free-text sector, e.g. "fintech". Drives Nice-class inference (when niceClasses is empty) and the human-readable recommendation. |
language | "en" | "fr" | "en" | Drives the phonetic encoder. |
riskThreshold | integer | 60 | Minimum similarity (0–100) for a mark to be listed in the output. |
enableDomainCheck | boolean | true | Runs the RDAP co-check across .com/.io/.ai/.dev. |
Generation-mode fields (only used when positioning is set)
| Field | Type | Default | Description |
|---|---|---|---|
positioning | string | — | Free-text brand positioning / value proposition. Setting this (with names empty) switches the run to generate mode. |
tone | enum | Professional | One of Professional, Playful, Premium, Technical, Friendly. |
techniques | string[] | [] | Naming techniques to bias toward, e.g. ["compound", "invented"]. Empty = let the model choose. |
mustInclude / avoid | string[] | [] | Fragments every generated name must contain at least one of / must never contain. Enforced deterministically after generation (not just asked of the model); rejects are free. |
count | integer | 10 | How many names to generate before filtering (hard cap 20). |
maxLength | integer | 18 | Max generated-name length, excluding spaces (hard cap 30). |
bannedWords | string[] | [] | Extra words to reject beyond the built-in profanity list. |
Bring-your-own LLM (v1.8.0)
Generate mode needs an LLM key. There are two ways to supply one — per-run user overrides win, the operator env var is the fallback:
| Field | Type | Default | Description |
|---|---|---|---|
llmProvider | enum | openai | openai = any OpenAI-compatible endpoint (OpenRouter, OpenAI, Groq, Ollama, LM Studio). anthropic = native Claude Messages API. |
llmApiKey | string | — | Your own provider API key (marked secret — never logged). Overrides the operator's OPENROUTER_API_KEY. |
llmBaseUrl | string | provider default | Provider endpoint, HTTPS (plain HTTP only for localhost). Overrides OPENROUTER_BASE_URL. |
llmModel | string | operator default | Single model id used for both primary and fallback. Examples: anthropic/claude-3.5-sonnet (OpenRouter), llama-3.3-70b-versatile (Groq), gpt-4o-mini (OpenAI), claude-haiku-4-5-20251001 (Anthropic). |
If you supply llmApiKey, you pay your own LLM provider directly. If you leave
these blank, the run uses the operator-configured OPENROUTER_API_KEY (the Actor
owner's) — but only on the default OpenRouter endpoint with the default
openai provider. Setting llmBaseUrl to any non-default endpoint, or
llmProvider to anthropic, requires your own llmApiKey: the operator's
key is never transmitted to a custom endpoint or a non-default provider (key
custody). A run that omits it fails fast with ERR_MISSING_API_KEY — it never
silently falls back to the operator key. Screen-only runs (names set) never
read any key.
Provider examples (set as input, or as the operator env vars):
// Groq (fast + cheap) — OpenAI-compatible{ "llmProvider": "openai", "llmApiKey": "gsk_…", "llmBaseUrl": "https://api.groq.com/openai/v1/chat/completions", "llmModel": "llama-3.3-70b-versatile" }// OpenAI direct{ "llmProvider": "openai", "llmApiKey": "sk-…", "llmBaseUrl": "https://api.openai.com/v1/chat/completions", "llmModel": "gpt-4o-mini" }// Anthropic (native Claude API) — no OpenRouter middleman{ "llmProvider": "anthropic", "llmApiKey": "sk-ant-…", "llmModel": "claude-haiku-4-5-20251001" }// OpenRouter (operator default) — leave llm* blank, or set your own OpenRouter key{ "llmProvider": "openai", "llmApiKey": "sk-or-…", "llmModel": "anthropic/claude-3.5-sonnet" }
If neither a user
llmApiKeynor the operatorOPENROUTER_API_KEYis set, generate mode fails fast withERR_MISSING_API_KEY— it never silently falls back to a weaker model.
Example input — screen mode
{"names": ["Teslaa", "Acme"],"jurisdictions": ["FR", "US", "EU"],"niceClasses": ["9", "12", "42"]}
Example input — generate mode
{"positioning": "a calmer way to manage freelance invoices","industry": "fintech","tone": "Professional","count": 10}
Output
The Actor writes to two places:
- Dataset — one row per name × selected jurisdiction.
- Key-value store
OUTPUT—{ schemaVersion, runId, mode, records[], filtered[]? }. This is the record AI agents should read first;modeis"screen"or"generate", andfilteredis only present in generate mode.
Dataset fields
| Field | Meaning |
|---|---|
name / normalizedName | The screened name, and its normalized comparison form. |
jurisdiction | FR / US / EU / WO / UK. |
source | Registry label, e.g. "INPI". |
screeningStatus | "screened" or "not_screened" — never a false clear. |
risk | "clear" / "caution" / "high-risk" / "not_screened". |
ipRiskScore | 0–100 collision risk for this jurisdiction; higher = more likely to conflict. |
topSimilarMark / topSimilarity | The closest colliding mark and its 0–100 similarity score, or null. |
similarMarksJson | JSON-encoded array of all similar marks with their visual/phonetic breakdown. |
niceClasses / niceClassesSource | Classes the screening was scoped to, and whether they were user-supplied or inferred from industry. |
marksScreened | How many in-scope marks were actually scored for this row (coverage honesty). A clear over 3 marks ≠ a clear over 150. 0 when not_screened. |
notScreenedReason | Typed reason when screeningStatus is not_screened (no_source / name_not_latin_screenable / name_has_no_distinctive_core / budget_exhausted / source_error / confusable_script); null when screened. |
runId | Apify run ID for traceability. |
disclaimer | The mandatory "not legal advice" disclaimer. |
Structured OUTPUT record fields
Each item in records[] carries: schemaVersion, name, normalizedName, brandReadiness
(worst risk band across all screened jurisdictions), ipRiskScore, recommendation
(human-readable), jurisdictions[] (per-jurisdiction verdict + similar marks, each with
marksScreened — the in-scope mark count behind that verdict), niceClassesSource,
matchedKeywords (the industry keywords that matched when classes were inferred; empty when
user-supplied), domainAvailability ({ tlds, results[], summary: { free, taken, error } }),
attorneyReview ({ recommended, message } — informational escalation only, see
FAQ), seedAsOf (the YYYY-MM the curated famous-mark seeds were last
verified — the seeds are point-in-time, not a live registry), and disclaimer.
In generate mode, each record additionally carries a generation block (technique,
brandFit, readability, memorability, compositeScore), and records[] is sorted by
compositeScore descending. Candidates rejected by the Tier-1 filter (profanity / banned word /
duplicate / missing_required_fragment / forbidden_fragment) never reach the screen — they're
listed separately under OUTPUT.filtered as { name, normalizedName, invalidReason }.
Example output — screen mode
Real run: { "names": ["Teslaa"], "jurisdictions": ["FR"] } correctly flags a phonetic collision
with the real seed mark Tesla (visual similarity alone would score this lower; phonetics catch
it):
{"schemaVersion": "1.6.0","name": "Teslaa","normalizedName": "teslaa","brandReadiness": "high-risk","ipRiskScore": 92,"recommendation": "High collision risk: \"Teslaa\" is phonetically close to \"Tesla\" (INPI, classes 12/9). Get trademark clearance before filing or investing in the name.","jurisdictions": [{"jurisdiction": "FR","source": "INPI","screeningStatus": "screened","risk": "high-risk","ipRiskScore": 92,"marksScreened": 163,"similarMarks": [{"mark": {"mark": "Tesla","jurisdiction": "WO","niceClasses": [12, 9],"status": "registered","owner": "Tesla, Inc."},"score": 92,"similarity": { "visual": 83, "phonetic": 100 },"signal": "phonetic"}]}],"niceClassesSource": "inferred","matchedKeywords": ["automotive"],"domainAvailability": {"tlds": ["com", "io", "ai", "dev"],"results": [{ "domain": "teslaa.com", "tld": "com", "status": "taken" },{ "domain": "teslaa.io", "tld": "io", "status": "available" },{ "domain": "teslaa.ai", "tld": "ai", "status": "available" },{ "domain": "teslaa.dev", "tld": "dev", "status": "available" }],"summary": { "free": 3, "taken": 1, "error": 0 }},"attorneyReview": {"recommended": true,"message": "High collision risk detected. We recommend engaging a qualified trademark attorney for a full clearance opinion before filing or investing further in this name. This screening is informational only and does not constitute legal advice."},"disclaimer": "Informational screening only — not legal advice. Registry-free ≠ trademark-free ≠ right-to-use. Clearance requires a qualified trademark attorney."}
Example output — generate mode
Real run: { "positioning": "a calmer way to manage freelance invoices", "count": 2 }. Note
brewforge outranks alphavault despite a lower brandFit — its higher readability and
memorability win out in the composite:
{"schemaVersion": "1.6.0","runId": "abc123","mode": "generate","records": [{"name": "brewforge","normalizedName": "brewforge","brandReadiness": "caution","ipRiskScore": 42,"generation": {"technique": "compound","brandFit": 90,"readability": 87,"memorability": 85,"compositeScore": 51}},{"name": "alphavault","normalizedName": "alphavault","brandReadiness": "caution","ipRiskScore": 43,"generation": {"technique": "compound","brandFit": 85,"readability": 88,"memorability": 80,"compositeScore": 48}}],"filtered": [{ "name": "AlphaVault", "normalizedName": "alphavault", "invalidReason": "duplicate" },{ "name": "shitstorm", "normalizedName": "shitstorm", "invalidReason": "profanity" }]}
(jurisdictions, domainAvailability, niceClassesSource, attorneyReview, and disclaimer
are present on every record exactly as in screen mode — omitted above for brevity.)
How it works
- Visual similarity — Damerau-Levenshtein edit distance on the normalized form (handles transpositions, not just insert/delete/substitute).
- Phonetic similarity — Double Metaphone for English; a French pre-processing pass (silent
finals, nasal vowels,
ph/ch/qudigraphs) feeding the same encoder for French. Each name gets a[primary, secondary]code pair; the max-of-pairs comparison means a partial match in either code still scores above zero. - Fusion — visual and phonetic scores are combined into one
ipRiskScore, scoped to the requested Nice classes, with banding intoclear(no collision found in the screened seed),caution, orhigh-risk. - Domain check — RDAP (the IETF successor to WHOIS) queried directly per TLD, bounded
concurrency, with a timeout; network failures degrade to
errorentries and never throw. - Generation — an OpenRouter-routed LLM (DeepSeek primary, GPT-4o-mini fallback, one retry on malformed JSON before falling back, schema-validated output) proposes names against your positioning brief; everything downstream runs through the same deterministic filter and screening pipeline as screen mode.
Usage via API
Run the Actor programmatically with the Apify API client. Use
Protocol/brand-name-ip-screener as this Actor's ID, and supply your
Apify API token.
Python
$pip install apify-client
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_API_TOKEN>")run_input = {"names": ["Teslaa", "Acme"],"jurisdictions": ["FR", "US", "EU"],"industry": "fintech",}run = client.actor("Protocol/brand-name-ip-screener").call(run_input=run_input)for item in client.dataset(run["defaultDatasetId"]).iterate_items():print(item["name"], item["jurisdiction"], item["risk"], item["ipRiskScore"])output = client.key_value_store(run["defaultKeyValueStoreId"]).get_record("OUTPUT")print(output["value"])
JavaScript / TypeScript
$npm install apify-client
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<YOUR_APIFY_API_TOKEN>' });const run = await client.actor('Protocol/brand-name-ip-screener').call({positioning: 'a calmer way to manage freelance invoices',industry: 'fintech',count: 10,});const { items } = await client.dataset(run.defaultDatasetId).listItems();console.table(items.map((i) => ({ name: i.name, jurisdiction: i.jurisdiction, risk: i.risk })));
cURL
curl -X POST "https://api.apify.com/v2/acts/Protocol~brand-name-ip-screener/run-sync-get-dataset-items?token=<YOUR_APIFY_API_TOKEN>" \-H 'Content-Type: application/json' \-d '{ "names": ["Brandcraft"], "jurisdictions": ["FR"] }'
Use with AI agents (MCP)
The Actor is built to be driven by AI agents. INPUT_SCHEMA.json exposes typed fields with enum
dropdowns and defaults, so an MCP client (e.g. Claude Desktop via the
Apify MCP Server) can call it with minimal
prompting. Agents should read the structured OUTPUT record — it carries brandReadiness,
recommendation, and attorneyReview per name, so the agent doesn't have to reduce the flat
dataset itself. Invalid input returns a structured error code rather than a silent failure (see
Run-level error codes).
{"mcpServers": {"apify": {"command": "npx","args": ["mcp-remote","https://mcp.apify.com/?tools=Protocol/brand-name-ip-screener","--header","Authorization: Bearer <YOUR_APIFY_API_TOKEN>"]}}}
Tool-call example (screen mode)
Once the apify MCP server above is connected, an agent invokes the screener as a tool call. The
tool name exposed by the Apify MCP Server is the Actor identifier (Protocol/brand-name-ip-screener);
the argument is the same input shape as Example input — screen mode.
// Agent → MCP tool call{"tool": "Protocol/brand-name-ip-screener","input": {"names": ["Teslaa", "Acme"],"jurisdictions": ["FR", "US", "EU"],"niceClasses": ["9", "12", "42"],"enableDomainCheck": true}}
The tool returns the flat per-name × jurisdiction dataset rows plus a reference to the OUTPUT
key-value record. Agents should read OUTPUT first — it carries the per-name brandReadiness,
recommendation, and attorneyReview verdicts so the agent does not have to reduce the flat
dataset itself. See Branching on the OUTPUT record
for the decision fields, and Run-level error codes for the coded
failure shape. Invalid input returns a coded error (e.g. ERR_INVALID_INPUT) rather than a silent
failure — branch on the ERR_* prefix, not on free text.
Branching on the OUTPUT record (agent guidance)
When driving this Actor from a tool-calling agent, branch on these fields rather than free-text parsing:
brandReadiness— the overall verdict across all screened jurisdictions:clear|caution|high-risk|not_screened. Treatnot_screenedas "no signal", not as "safe" — it means no registry source ran for this name.jurisdictions[].screeningStatus—screenedvsnot_screenedper jurisdiction. Onlyscreenedrows were actually compared against marks; anot_screenedrow carries areason(No data source enabled…,name_not_latin_screenable,name_has_no_distinctive_core, orbudget_exhausted).jurisdictions[].marksScreened— the denominator behind each verdict: how many in-scope marks were actually scored. Aclearover 3 marks is not the same confidence as aclearover 150. Weight aclearbymarksScreenedbefore reporting low collision risk — aclearis "no collision found among the marks screened", not a promise of clearance.niceClassesSource+matchedKeywords— whether the Nice-class scope wasuser-supplied orinferredfromindustry, and which industry keywords matched. IfinferredandmatchedKeywordsis empty, the screen ran across all classes (noisier) — consider asking the user for explicitniceClassesbefore relying on aclearas a low-collision signal.attorneyReview.recommended— a structured boolean for high-risk escalation messaging (informational only; no referral, see FAQ).domainAvailability.summary—{ free, taken, error }across.com/.io/.ai/.dev.
Billing per event: every screened jurisdiction in screen mode bills screening_report_produced
($1.00). Generate mode is free — every filter-survivor emits
candidate_generated_and_rescreened at $0.00. not_screened rows and filter rejects are free too.
If cost matters, scope with niceClasses and limit jurisdictions.
Run-level error codes
Every run failure exits with a coded Actor.fail message prefixed by an ERR_* code, so an MCP
agent can branch deterministically rather than parsing free text. The table below enumerates every
code defined in src/lib/errors.ts plus the runtime catch-all emitted by src/main.ts.
| Code | Meaning | When it fires | What an agent consumer should do |
|---|---|---|---|
ERR_INVALID_INPUT | Input failed schema validation; names and positioning were both empty OR both set; or a niceClasses entry was not an integer in 1–45. | At input validation, before any network/billing work. Raised by src/input/validate.ts (InvalidInputError). | Surface to user + retry. Do not retry the same payload. Ask the user to fix the flagged field(s), set exactly one of names / positioning, or correct niceClasses. No charge has been made. |
ERR_MISSING_API_KEY | Generate mode was requested but no LLM key is configured — neither a user llmApiKey nor the operator OPENROUTER_API_KEY. Also raised when a custom llmBaseUrl or non-default llmProvider is set without its own llmApiKey (the operator key is never sent to a custom endpoint — key custody). | At LLM router init, generate mode only. Raised by src/generation/router.ts (MissingApiKeyError). Screen-only runs never hit this. | Surface to user + retry. Ask the user for a llmApiKey (bring-your-own), or to switch back to the default openai provider + default endpoint so the operator key can be used. |
ERR_LLM_PIPELINE_FAILED | Both the primary and fallback LLM models failed or timed out (generate mode only). | After both generation attempts exhaust retries / timeout / malformed-JSON recovery in src/generation/router.ts (LlmPipelineError). | Retry once with backoff, then surface to user. If it persists, suggest a different llmModel / provider, or fall back to screen mode by supplying names directly. |
ERR_REGISTRY_SOURCE | A registry mark source threw (network / parse / auth failure) for a jurisdiction. | Re-thrown by src/registry/seedAdapter.ts (RegistrySourceError) when a MarkSource rejects; caught per-jurisdiction in src/main.ts, which surfaces that jurisdiction as not_screened with reason source_error and logs it. The run continues. | Partial result. Treat the affected jurisdiction as not_screened (no verdict), not as clear. Retry the run; if it persists for one jurisdiction only, screen without it or report via the Issues tab. |
ERR_OUTPUT_SCHEMA_VIOLATION | An internal output record did not match the declared dataset / OUTPUT schema. | Raised by src/output/datasetValidator.ts (OutputSchemaViolationError) right before Actor.pushData, after AJV-validating every flat dataset row against the published .actor/dataset_schema.json (B4). The run fails the moment a row breaks the public contract. | Stop + report. This is an actor-side defect, not a user-input issue. Do not retry the same input; surface the run id to the user and direct them to the Actor's Issues tab. |
ERR_UNEXPECTED | Any non-coded throw after Actor.init — an unanticipated runtime failure (network blip, adapter crash, etc.). | Catch-all in src/main.ts: any Error without an ERR_* code is wrapped in a typed UnexpectedError (src/lib/errors.ts, exported from ERROR_CODES.UNEXPECTED since B4) before Actor.fail, then rethrown so the platform records the non-zero exit. | Retry once. If it persists, surface to the user and report via the Actor's Issues tab with the run id. Never assume data was produced — re-check the dataset / OUTPUT on retry. |
Informational screening only — not legal advice. None of these codes is a trademark verdict. A
clear/caution/high-riskresult is carried inOUTPUT.records[].brandReadiness, not in the error channel. See FAQ for the legal boundary.
FAQ
Why does the same name sometimes get a different risk score per jurisdiction? Each jurisdiction is screened against its own curated mark dataset, so a name can collide with a real mark in one jurisdiction's sample and not another's. This is expected, not a bug.
What does marksScreened tell me, and why does a clear come with a number?
marksScreened is the count of in-scope marks actually scored for a verdict (after the Nice-class
filter). A clear over 3 marks is a much weaker signal than a clear over 150, so every verdict
carries its denominator. Read clear as "no collision found in the screened seed," not as
"trademark-free" — a real registry may hold a conflicting mark that isn't in the curated sample.
What does attorneyReview.recommended: true actually get me?
Nothing beyond the message itself — it's a flag, not a referral. There is no attorney network,
fee, or law-firm relationship behind it; it's the same "get clearance" advice as the
recommendation field, surfaced as a structured boolean an agent can branch on.
Why is niceClassesSource sometimes "inferred" even though I set industry?
niceClassesSource is "inferred" whenever niceClasses was left empty — the engine then derives
classes from a curated keyword table keyed on industry (e.g. "fintech" → classes 9, 36, 42).
Set niceClasses explicitly if you want "user" provenance and tighter scoping.
Will the same positioning always generate the same names?
No. LLM generation is non-deterministic — the same input can produce different candidates across
runs. The trademark/domain verdicts for a given generated name are deterministic.
Can a domain marked available be taken later?
Yes. RDAP results are a point-in-time snapshot. Re-confirm before relying on it.
Is my input data sent anywhere?
In generate mode, positioning/industry/tone/etc. are sent to a third-party LLM provider (via
OpenRouter) to generate candidates. Screen mode sends nothing externally except the RDAP domain
probes (no credentials, no PII).
Known limitations
| Limitation | What to expect |
|---|---|
| Curated seed data, not full registries | Famous-mark collision screening only — a clear verdict means no collision found in the curated seed, not "trademark-free". A name can still conflict with a real mark not in the sample. Weight a clear by the per-jurisdiction marksScreened count. |
| Non-deterministic generation | The same positioning may produce different candidates on different runs. |
| Point-in-time domain availability | A TLD marked available may be claimed before you register it. |
| Max 20 names/candidates per run | Hard cap; larger batches require multiple runs. |
| Non-Latin names are not screenable | A name that normalizes to an empty core is surfaced as not_screened — reason name_not_latin_screenable when its letters are all non-Latin (CJK / Cyrillic), or name_has_no_distinctive_core when it carries no script at all (punctuation / emoji only). Never scored as a false clear, never billed. |
| Lookalike-script names are not screenable | A name mixing Latin with a lookalike script (Cyrillic, Greek, Armenian, Cherokee, fullwidth or mathematical letterforms — e.g. Gоogle with a Cyrillic o, or Google with a fullwidth G) is surfaced as not_screened with reason confusable_script. Normalizing it would silently screen a different name, so it is never scored and never billed. |
| No social-handle check | Domain availability is checked; GitHub/X/Twitch/etc. handle availability is not (see the sibling AI Username Generator Actor). |
Data & privacy
- Generate mode only:
positioning,industry,tone,techniques,mustInclude, andavoidare sent to a third-party LLM provider (via OpenRouter) to generate candidates. Do not include personal data you don't want shared with an LLM provider. - Domain checks are unauthenticated — no credentials are sent to any registry, and no raw external response bodies are stored in your dataset or logs.
- Data retention: datasets are retained per your Apify account settings. Delete the run to erase its outputs.
Develop
pnpm installpnpm test # unit tests (no network — RDAP/LLM sources are injected)pnpm type-checkpnpm lint
RDAP environment variables (optional)
Copy .env.example to .env and uncomment to override:
| variable | default | effect |
|---|---|---|
RDAP_ENABLED | true | set to false to skip the network calls |
RDAP_TIMEOUT_MS | 5000 | per-request HTTP timeout |
RDAP_CONCURRENCY | 4 | max simultaneous RDAP probes |
Generation environment variables
Operator-side, only read in generate mode (positioning set) and only when the
run does not supply its own llmApiKey (see Bring-your-own LLM).
On Apify, set these as secret environment variables (Console → Settings →
Environment variables) — they are injected at run time and never baked into the
build image or exposed to users.
| variable | default | effect |
|---|---|---|
OPENROUTER_API_KEY | — | required for generate mode when no user llmApiKey is supplied; the operator's LLM key. |
OPENROUTER_BASE_URL | OpenRouter's chat-completions endpoint | must be HTTPS (plain HTTP allowed for localhost only). Ignored for llmProvider: "anthropic" runs. |
LLM_PRIMARY_MODEL | deepseek/deepseek-chat | primary generation model. A user-supplied llmModel overrides both primary and fallback. |
LLM_FALLBACK_MODEL | openai/gpt-4o-mini | fallback model on primary failure/timeout/malformed JSON. |
LLM_TIMEOUT_MS | 8000 | per-request timeout for both models. |
Contributing
Source: github.com/m-raphael/brand-name-ip-screener.
Contributions are welcome — see ./CONTRIBUTING.md for setup, the test-first
workflow, the schema-coordination rules, and the hard invariants ("never a false clear", the UPL
boundary, no referral/fee logic in this Actor). The repo runs CI (type-check, lint, network-free
tests, build) on every pull request.
Seed-data provenance, the third-party-trademark notice, and coverage limits are documented in ./DATA-SOURCES.md — read that before relying on a verdict, and use it to file a correction if a seed entry is wrong.
Security
Found a security issue — especially anything touching key custody (the operator
OPENROUTER_API_KEY must never reach a user-supplied endpoint)? Do not open a public issue;
see ./SECURITY.md for the private reporting channel and the bug classes that matter
most here.
Support
Found a bug or want a jurisdiction added? Open an issue on the Actor's Issues tab in the Apify Console — that is the primary support channel — or on GitHub. Security reports go through ./SECURITY.md, not public issues.